КулЛиб - Классная библиотека! Скачать книги бесплатно 

Технологии интеграции 1С:Предприятия 8.3 [Елена Юрьевна Хрусталева] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]
Хрусталева Е.Ю.

Технологии интеграции
1С:Предприятия 8.3
Электронная книга в формате pdf; ISBN 978-5-9677-2964-5.

Книга адресована специалистам, имеющим опыт разработки на платформе «1С:Предприятие».
С ее помощью можно освоить механизмы «1С:Предприятия 8.3», предназначенные для обмена
данными в распределенных системах, а также механизмы и технологии, позволяющие
интегрировать прикладные решения с другими информационными системами, не
использующими «1С:Предприятие».
В период активного перехода на удаленную работу особую ценность приобретают доработки,
которые реализуют функции автоматического взаимодействия прикладного решения с
внешними системами и ресурсами. Например, важным ресурсом являются Интернет-сайты,
поскольку основной инструмент потенциальных клиентов в этот период – браузер. Если, заходя
на сайт, клиенты смогут, например, самостоятельно размещать заказы в программе 1С или
получать из нее некоторую информацию, это может значительно облегчить взаимодействие с
ними без использования офисной телефонной связи. В этой книге как раз подробно
рассматриваются все ключевые технологии, которые есть в системе «1С:Предприятие» для
интеграции с различными внешними системами.
По сравнению с предыдущим изданием «Технологии интеграции "1С:Предприятия 8.2"») книга
дополнена описанием интернет-технологий, которые появились в платформе 8.3 или не были
описаны раньше:
• JSON;
• HTTP-сервисы (REST);
• HTTP-запросы;
• автоматический REST-интерфейс (OData);
• Web-сервисы;
• FTP-соединение;
• электронная почта.
Для создания демонстрационных примеров использована версия 8.3.16.1030 платформы
«1С:Предприятие 8».
Книга выпущена под редакцией Максима Радченко.
Дополнительные материалы
Информационные базы с примерами, описанными в книге, опубликованы на портале 1С:ИТС.
Вы можете скачать их по адресу http://its.1c.ru/book_demo/.
Интернет-конференция для начинающих разработчиков http://devtrainingforum.v8.1c.ru/forum.

Оглавление

Введение.................................................................................................................7
Глава 1. Интернет-технологии.............................................................................9
JSON..................................................................................................................................................9
Общая информация.....................................................................................................................9
Потоковая работа....................................................................................................................... 11
Сериализация коллекций значений (объектная техника).......................................................21
Сериализация прикладных типов «1С:Предприятия»............................................................37
Смешанная техника работы......................................................................................................44
HTTP-сервисы (REST)....................................................................................................................46
Общая информация...................................................................................................................47
Разработка HTTP-сервиса.........................................................................................................49
Примеры реализации HTTP-сервисов......................................................................................50
HTTP-сервисы в расширениях..................................................................................................72
HTTP-запросы.................................................................................................................................73
Обращение к HTTP-сервисам...................................................................................................76
Обращение к REST-интерфейсу (OData).................................................................................81
Автоматический REST-интерфейс (OData)..................................................................................81
Общая информация...................................................................................................................81
Правила формирования URL запроса......................................................................................83
Примеры использования...........................................................................................................88
Типичные ошибки при получении данных..............................................................................121

4

Технологии интеграции «1С:Предприятия 8.3»

Web-сервисы.................................................................................................................................125
Общая информация.................................................................................................................125
Предоставление функциональности через Web-сервисы....................................................128
Работа с Web-сервисами сторонних поставщиков................................................................131
Пример реализации Web-сервиса..........................................................................................133
Web-сервисы в расширениях..................................................................................................144
Повторное использование сеансов интернет-сервисов............................................................144
Автоматическое переиспользование сеансов.......................................................................147
Ручное управление сеансами.................................................................................................148
Коды состояния в ответах HTTP-сервера..................................................................................149
FTP-соединение...........................................................................................................................151
Получить файлы с сервера.....................................................................................................153
Записать файлы на сервер.....................................................................................................154
Копировать файлы с сервера.................................................................................................155
Электронная почта.......................................................................................................................158
Отправить и получить почту....................................................................................................159
Отправить и получить сообщение обмена.............................................................................165
Глава 2. Внешние источники данных.............................................................171
Работа с реляционными внешними источниками данных........................................................172
Общая информация.................................................................................................................172
Строка соединения...................................................................................................................174
Редактирование структуры внешнего источника данных.....................................................176
Работа с функциями внешнего источника данных................................................................183
Управление внешними источниками данных.............................................................................186
Примеры использования.............................................................................................................188
Исходная информация для примеров....................................................................................189
DSN............................................................................................................................................192
Работа с внешними источниками данных
в конфигураторе и в режиме «1С:Предприятие»..................................................................194
Программная синхронизация..................................................................................................205
Работа с функциями................................................................................................................ 211
Прикладное использование данных из внешних источников...............................................220
Глава 3. Обмен данными..................................................................................227
Планы обмена..............................................................................................................................229
Служба регистрации изменений.............................................................................................232
Инфраструктура сообщений....................................................................................................247
Распределенные информационные базы..................................................................................251
Общие принципы......................................................................................................................251
Главный и подчиненный узлы.................................................................................................254
Сообщение обмена данными
в распределенной информационной базе.............................................................................255

Оглавление

5

Работа с распределенной информационной базой..............................................................258
Подготовка конфигурации к работе
в распределенной информационной базе.............................................................................261
Пример реализации обмена данными
в распределенной информационной базе.............................................................................264
Сценарии обмена данными
в распределенной информационной базе.............................................................................270
Доработка примера обмена данными
в распределенной информационной базе.............................................................................275
Особенности использования последовательности документов
в распределенной информационной базе.............................................................................288
Универсальный механизм обмена данными..............................................................................290
Использование возможностей работы с XML-документами.................................................290
Пример реализации универсального обмена........................................................................293
Использование транзакций при организации обмена...............................................................333
Методика включения в сообщение обмена дополнительной информации............................335
Организация одностороннего обмена........................................................................................338
Примеры реализации автоматического обмена данными........................................................340
Использование регламентных заданий..................................................................................340
Использование объекта «СОМСоединение».........................................................................341
Использование планов обмена в расширении конфигурации..................................................342
Универсальный способ обмена данными..............................................................................343
Обмен данными в распределенной информационной базе.................................................348
Глава 4. Внешние компоненты........................................................................353
Подключение внешней компоненты в тонком клиенте или в веб-клиенте
(на примере Native API компоненты)..........................................................................................355
Подключение внешней компоненты из файла на диске (отдельные файлы).....................356
Подключение внешней компоненты из макета (ZIP-архив)..................................................356
Подключение внешней компоненты из базы данных (ZIP-архив)........................................356
Подключение внешней компоненты
в толстом клиенте или на сервере (на примере Native API компоненты)................................357
Подключение внешней компоненты из файла на диске (отдельные файлы).....................357
Подключение внешней компоненты из макета (ZIP-архив)..................................................357
Подключение внешней компоненты из макета (отдельные файлы)...................................357
Подключение внешней компоненты из базы данных (ZIP-архив)........................................358
Подключение внешней компоненты из базы данных (отдельные файлы).........................358
Глава 5. Взаимодействие
с приложением системы «1С:Предприятие»................................................359
Automation.....................................................................................................................................359
Automation Server.....................................................................................................................359
Automation Client.......................................................................................................................365
Внешнее соединение...................................................................................................................367

6

Технологии интеграции «1С:Предприятия 8.3»

Встраивание веб-клиента «1С:Предприятия» в сторонний сайт..............................................370
Общая информация.................................................................................................................370
Пример реализации.................................................................................................................372
Работа с локальной файловой системой...................................................................................381
Глава 6. Файловое взаимодействие..............................................................381
Найти файлы в каталоге..........................................................................................................382
Удалить файлы в каталоге......................................................................................................384
Создать новый каталог............................................................................................................385
Копировать файл......................................................................................................................387
Переместить файл...................................................................................................................389
Передача файлов между клиентом и сервером........................................................................391
Передача и получение одного файла с сервера...................................................................392
Передача нескольких файлов на сервер...............................................................................395
Получение нескольких файлов с сервера..............................................................................398
Текстовые файлы.........................................................................................................................400
Текстовый документ, поле текстового документа.................................................................401
Отображение текстового документа.......................................................................................404
Модель последовательного доступа......................................................................................406
XML-файлы...................................................................................................................................408
Основные положения...............................................................................................................408
Базовые средства «1С:Предприятия» для работы с XML....................................................413
XML-сериализация...................................................................................................................420
HTML-документ ............................................................................................................................443
Поле HTML-документа.............................................................................................................445
Объектная модель документа.................................................................................................446
Примеры работы......................................................................................................................448
Двоичные данные.........................................................................................................................463
Общая информация.................................................................................................................464
Примеры работы......................................................................................................................466
XDTO-сериализация.....................................................................................................................479
ZIP-архивы....................................................................................................................................482
Создание архива......................................................................................................................482
Чтение ZIP-архивов..................................................................................................................487
Работа с файлами большого объема.....................................................................................488
Примеры работы......................................................................................................................489
DBF-файлы...................................................................................................................................498

Введение

В книге собрана и систематизирована наиболее важная
информация, которая может понадобиться разработчику прикладных
решений «1С:Предприятия 8.3».
Несмотря на то что в одной книге невозможно рассмотреть все
ситуации, возникающие при разработке прикладных решений,
на большинство вопросов в книге можно найти ответы. Причем
читать будет одинаково интересно как начинающим, так и продвинутым разработчикам.
При написании мы стремились к тому, чтобы книга стала серьезным
инструментом для разработчиков: к ней всегда можно было бы
обратиться в случае затруднений, узнать что-то новое о хорошо
известной предметной области или познакомиться с новым взглядом
на привычные вещи.
При подготовке материала были использованы различные источники
информации:
■■ опыт преподавания на учебных курсах по платформе и прикладным
решениям «1С:Предприятия 8»;
■■ опыт внедрения прикладных решений;
■■ опыт, накопленный разработчиками фирмы «1С»;

8

Технологии интеграции «1С:Предприятия 8.3»

■■ материалы информационно-технологической поддержки (ИТС);
■■ материалы форума партнеров-разработчиков на сайте
http://partners.v8.1c.ru;
■■ общение на партнерских семинарах, проводимых фирмой «1С».

Глава 1.
Интернет-технологии

JSON
В настоящее время в веб-приложениях широко используется формат обмена
данными JSON. JSON (JavaScript Object Notation) – это текстовый формат
обмена данными, с которым могут работать все браузеры. Этот формат похож
на XML, но по сравнению с XML он является более лаконичным и требует
меньше места.

Общая информация
Есть несколько причин для широкого использования этого формата
на уровне платформы. Во-первых, JSON – это современный формат,
с помощью которого прикладные решения «1С:Предприятия» могут
осуществлять интеграцию со сторонними приложениями. Во-вторых,
JSON активно используется в HTTP-интерфейсах, а платформа «1С:Предприятие 8» как раз предоставляет два способа реализации таких интерфейсов – это REST-интерфейс, который автоматически формируется для
всего прикладного решения, и HTTP-сервисы, которые можно создавать
самостоятельно.

10

Технологии интеграции «1С:Предприятия 8.3»

Существует несколько основных сценариев использования JSON:
■■ Интеграция с внешними системами через их HTTP-интерфейсы: Google
Calendar, Salesforce.com, REST-интерфейс «1С:Предприятия», SharePoint
и т. д.
■■ Организация собственного HTTP-интерфейса прикладного решения.
■■ Обмен файлами JSON с внешними системами. Формирование конфигурационных, настроечных файлов. Использование их в процедурах обмена
данными, например с интернет-магазинами.
■■ Использование файлов JSON для обмена данными между разными приложениями «1С:Предприятия».
JSON – это текстовый формат, поэтому данные в формате JSON могут содержать:
■■ Объект – неупорядоченное множество пар :,
заключенный в фигурные скобки ({}). Пары :
разделяются запятыми (,).
■■ Массив – множество значений. Массив заключается в квадратные скобки
([]). Значения разделяются запятыми (,).
■■ Значение – может быть строкой, числом, объектом, массивом или литералом true, false, null.
□□ Строка – набор символов, заключенный в двойные кавычки («»).
□□ Число – сериализуется с разделителем точка (.). Точность числа
не ограничена.
Таким образом, с помощью вышеперечисленных элементов допускается
описание объектов любой сложности для представления в формате JSON.
В платформе реализовано несколько слоев работы с JSON. Самые универсальные и гибкие – это низкоуровневые средства потоковой записи и чтения.
Более высокоуровневые и не такие универсальные – средства сериализации
в JSON примитивных типов и коллекций «1С:Предприятия». И, наконец,
третий слой – это средства, позволяющие сериализовать/десериализовать
прикладные типы «1С:Предприятия»: ссылки, объекты, наборы записей
и вообще любые типы, для которых поддерживается XDTO-сериализация
(про XDTO-сериализацию рассказывается в разделе «XDTO-сериализация»).
ПОДРОБНЕЕ
Более подробно про работу с форматом данных JSON рассказано
в главе 1 «Интернет-технологии».

Глава 1. Интернет-технологии

11

Подробнее познакомиться с примерами сериализации/десериализации
данных в формате JSON можно в демонстрационной конфигурации
«Примеры работы», которая прилагается к книге.

Потоковая работа
Для потоковой работы предназначены объекты ЧтениеJSON и ЗаписьJSON.
Они последовательно, от значения к значению, читают JSON из файла или
строки или последовательно записывают JSON в файл или строку. Таким
образом, чтение и запись JSON происходят без формирования всего документа в памяти. В результате потоковая техника может дать существенный
выигрыш, особенно если объем документа потенциально большой или же
если этот объем заранее неизвестен.
Запись и чтение
Для того чтобы выполнить потоковую запись JSON-документа, необходимы
записываемые данные и объект ЗаписьJSON.
Допустим, нам нужно записать в JSON-документ некоторый объект,
обладающий свойствами Код, Наименование, Телефоны (массив строк),
ОбъемПродаж, Поставщик?.
Один JSON-файл содержит одно значение. Это то самое значение, о котором
говорилось выше: объект, массив, строка, число или один из литералов.
Поэтому записываем в JSON наш объект следующим образом (листинг 1.1).
Листинг 1.1. Пример потоковой записи JSON-документа
&НаСервереБезКонтекста
Процедура ПотоковаяЗаписьНаСервере()
// Создать объект записи и открыть файл, в который будет выполняться запись.
Запись = Новый ЗаписьJSON;
Запись.ОткрытьФайл(″c:\temp\streamWrite.json″, , , Новый ПараметрыЗаписиJSON(, Символы.Таб));
// Выполнить запись значений с помощью объекта записи (Запись).
// Записать начало нашего объекта.
Запись.ЗаписатьНачалоОбъекта();
// Заполнить свойство Код типа Строка.
Запись.ЗаписатьИмяСвойства(″Код″);
Запись.ЗаписатьЗначение(″000000017″);
// Заполнить свойство Наименование типа Строка.
Запись.ЗаписатьИмяСвойства(″Наименование″);
Запись.ЗаписатьЗначение(″ОАО Топаз″);
// Заполнить свойство Телефоны типа Массив.

12

Технологии интеграции «1С:Предприятия 8.3»
// Поэтому после имени свойства записываем массив, состоящий из значений – строк.
Запись.ЗаписатьИмяСвойства(″Телефоны″);
Запись.ЗаписатьНачалоМассива();
Запись.ЗаписатьЗначение(″8-999-777-55-33″);
Запись.ЗаписатьЗначение(″+71112223344″);
Запись.ЗаписатьКонецМассива();
// Заполнить свойство ОбъемПродаж типа Число.
Запись.ЗаписатьИмяСвойства(″ОбъемПродаж″);
Запись.ЗаписатьЗначение(5000000);
// Заполнить свойство Поставщик? типа Булево.
Запись.ЗаписатьИмяСвойства(″Поставщик?″);
Запись.ЗаписатьЗначение(Ложь);
// Записать конец нашего объекта.
Запись.ЗаписатьКонецОбъекта();
// Завершить работу с файлом.
Запись.Закрыть();

КонецПроцедуры

Содержимое процедуры понятно из комментариев в тексте листинга.
Поэтому не будем еще раз на этом останавливаться. Про параметры записи
JSON, переданные четвертым параметром в метод ОткрытьФайл(), будет
рассказано ниже, в разделе «Параметры записи JSON».
В результате сформированный JSON-документ будет иметь следующий вид
(листинг 1.2).
Листинг 1.2. Содержимое JSON-документа
{

″Код″: ″000000017″,
″Наименование″: ″ОАО Топаз″,
″Телефоны″: [

″8-999-777-55-33″,

″+71112223344″
],
″ОбъемПродаж″: 5000000,
″Поставщик?″: false
}

Теперь представим, что мы хотим записать не какой-то абстрактный объект,
а объект, обладающий собственным именем – Контрагент1. Возможно, вместе
с ним мы захотим записать и другой аналогичный объект с именем Контрагент2. Как поместить это в один JSON-файл?
Поскольку, как мы говорили, JSON-файл может содержать только одно
значение, мы запишем в него корневой объект с единственным свойством.

Глава 1. Интернет-технологии

13

Имя этого свойства – это имя нашего объекта, а значение этого свойства – это
совокупность свойств, которыми обладает наш объект (листинг 1.3).
Листинг 1.3. Пример потоковой записи JSON-документа
&НаСервереБезКонтекста
Процедура ПотоковаяЗаписьНаСервере()
// Создать объект записи и открыть файл, в который будет выполняться запись.
Запись = Новый ЗаписьJSON;
Запись.ОткрытьФайл(″c:\temp\streamWrite_2.json″, , , Новый ПараметрыЗаписиJSON(, Символы.Таб));
// Выполнить запись значений с помощью объекта записи (Запись).
// Записать начало корневого объекта.
Запись.ЗаписатьНачалоОбъекта();
// Записать имя свойства корневого объекта.
Запись.ЗаписатьИмяСвойства(″Контрагент1″);
// Записать начало нашего объекта.
Запись.ЗаписатьНачалоОбъекта();
// Заполнить свойство Код типа Строка.
Запись.ЗаписатьИмяСвойства(″Код″);
Запись.ЗаписатьЗначение(″000000017″);
// Заполнить свойство Наименование типа Строка.
Запись.ЗаписатьИмяСвойства(″Наименование″);
Запись.ЗаписатьЗначение(″ОАО Топаз″);
// Заполнить свойство Телефоны типа Массив.
// Поэтому после имени свойтва записываем массив, состоящий из значений – строк.
Запись.ЗаписатьИмяСвойства(″Телефоны″);
Запись.ЗаписатьНачалоМассива();
Запись.ЗаписатьЗначение(″8-999-777-55-33″);
Запись.ЗаписатьЗначение(″+71112223344″);
Запись.ЗаписатьКонецМассива();
// Заполнить свойство ОбъемПродаж типа Число.
Запись.ЗаписатьИмяСвойства(″ОбъемПродаж″);
Запись.ЗаписатьЗначение(5000000);
// Заполнить свойство Поставщик? типа Булево.
Запись.ЗаписатьИмяСвойства(″Поставщик?″);
Запись.ЗаписатьЗначение(Ложь);
// Записать конец нашего объекта.
Запись.ЗаписатьКонецОбъекта();
// Записать конец корневого объекта.
Запись.ЗаписатьКонецОбъекта();
// Завершить работу с файлом.
Запись.Закрыть();
КонецПроцедуры

14

Технологии интеграции «1С:Предприятия 8.3»

В результате сформированный JSON-документ будет иметь следующий вид
(листинг 1.4).
Листинг 1.4. Содержимое JSON-документа
{

″Контрагент1″: {

″Код″: ″000000017″,

″Наименование″: ″ОАО Топаз″,

″Телефоны″: [

″8-999-777-55-33″,

″+71112223344″

],

″ОбъемПродаж″: 5000000,

″Поставщик?″: false
}
}

Если требуется записать в этот же файл второй объект Контрагент2, то нам
нужно просто добавить к корневому объекту еще одно свойство (Контрагент2)
и его значение (листинг 1.5).
Листинг 1.5. Пример потоковой записи JSON-документа
&НаСервереБезКонтекста
Процедура ПотоковаяЗаписьНаСервере()
// Создать объект записи и открыть файл, в который будет выполняться запись.
Запись = Новый ЗаписьJSON;
Запись.ОткрытьФайл(″c:\temp\streamWrite_3.json″, , , Новый ПараметрыЗаписиJSON(, Символы.Таб));
// Выполнить запись значений с помощью объекта записи (Запись).
// Записать начало корневого объекта.
Запись.ЗаписатьНачалоОбъекта();
// Записать имя первого свойства корневого объекта.
Запись.ЗаписатьИмяСвойства(″Контрагент1″);
// Записать начало первого объекта.
Запись.ЗаписатьНачалоОбъекта();

// Записать конец первого объекта.
Запись.ЗаписатьКонецОбъекта();
// Записать имя второго свойства корневого объекта.
Запись.ЗаписатьИмяСвойства(″Контрагент2″);
// Записать начало второго объекта.
Запись.ЗаписатьНачалоОбъекта();


Глава 1. Интернет-технологии

15

// Записать конец второго объекта.
Запись.ЗаписатьКонецОбъекта();
// Записать конец корневого объекта.
Запись.ЗаписатьКонецОбъекта();
// Завершить работу с файлом.
Запись.Закрыть();
КонецПроцедуры

В результате сформированный JSON-документ будет иметь следующий вид
(листинг 1.6).
Листинг 1.6. Содержимое JSON-документа
{

″Контрагент1″: {

″Код″: ″000000017″,

″Наименование″: ″ОАО Топаз″,

″Телефоны″: [

″8-999-777-55-33″,

″+71112223344″

],

″ОбъемПродаж″: 5000000,

″Поставщик?″: false
},
″Контрагент2″: {

″Код″: ″000000018″,

″Наименование″: ″ОАО Алмаз″,

″Телефоны″: [

″8-111-222-33-44″,

″+78886664422″

],

″ОбъемПродаж″: 6000000,

″Поставщик?″: true
}
}

Потоковое чтение JSON-документа выполняется с помощью объекта
ЧтениеJSON. Документ открывается для чтения и поэлементно считывается
в цикле. При этом разработчик определяет, что считано, и соответствующим
образом обрабатывает считываемые данные.
Простейший вариант чтения JSON-документа может быть выполнен
с помощью следующей процедуры (листинг 1.7).

16

Технологии интеграции «1С:Предприятия 8.3»

Листинг 1.7. Пример потокового чтения JSON-документа
&НаСервереБезКонтекста
Процедура ПотоковоеЧтениеНаСервере()
Сообщение = Новый СообщениеПользователю;
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\streamWrite_2.json″);
// Выполнить чтение поэлементно в цикле.
Пока Чтение.Прочитать() Цикл


Если Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.ИмяСвойства Тогда

Сообщение.Текст = ″Имя = ″ + Чтение.ТекущееЗначение;

Сообщение.Сообщить();

КонецЕсли;


Если Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.Булево Или

Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.Строка Или

Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.Число Или

Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.Комментарий Тогда

Сообщение.Текст = ″Значение = ″ + Чтение.ТекущееЗначение;

Сообщение.Сообщить();

КонецЕсли;
КонецЦикла;
// Завершить работу с файлом.
Чтение.Закрыть();
КонецПроцедуры

Результат чтения JSON-документа, содержимое которого показано в листинге
1.4, будет выглядеть следующим образом (рис. 1.1).

Рис. 1.1. Потоковое чтение JSON-документа

Глава 1. Интернет-технологии

17

Работа со строкой JSON
Данные в формате JSON можно получать не только в виде файла, но и в виде
строки. Например, при работе с HTTP-сервисами очень часто нужно работать
с телом HTTP-запроса, которое представляет собой строку JSON. В таких
случаях можно писать данные не в файл, а в строку. И потом подставлять
эту строку в качестве тела HTTP-запроса. И, наоборот, брать строку, которая
является телом HTTP-запроса, и читать из нее данные с помощью объекта
ЧтениеJSON.
Для этого нужно использовать метод УстановитьСтроку() объектов
ЗаписьJSON/ЧтениеJSON. После вызова этого метода для получения
строки со сформированными данными JSON достаточно просто завершить
запись документа методом Закрыть() объекта ЗаписьJSON (листинг 1.8).
Листинг 1.8. Запись и чтение содержимого JSON в/из строки
&НаСервереБезКонтекста
Процедура РаботаСоСтрокойНаСервере()
Сообщение = Новый СообщениеПользователю;
// Создать объект записи и записать строковое значение в строку JSON.
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
Запись.ЗаписатьЗначение(″строковое значение″);
СтрокаJSON = Запись.Закрыть();
// Показать результат.
Сообщение.Текст = СтрокаJSON;
Сообщение.Сообщить();
// Создать объект чтения и прочитать JSON из строки.
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(СтрокаJSON);
Пока Чтение.Прочитать() Цикл


Если Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.ИмяСвойства Тогда

Сообщение.Текст = ″Имя = ″ + Чтение.ТекущееЗначение;

Сообщение.Сообщить();

КонецЕсли;


Если Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.Булево Или

Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.Строка Или

Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.Число Или

Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.Комментарий Тогда

Сообщение.Текст = ″Значение = ″ + Чтение.ТекущееЗначение;

Сообщение.Сообщить();

КонецЕсли;
КонецЦикла;
// Завершить чтение.
Чтение.Закрыть();
КонецПроцедуры

18

Технологии интеграции «1С:Предприятия 8.3»

Проверка структуры записываемого JSON-документа
Следует иметь в виду, что при потоковой записи разработчик самостоятельно формирует структуру JSON-документа. При записи данных объект
ЗаписьJSON автоматически проверяет правильность записываемой структуры. За это отвечает свойство ПроверятьСтруктуру. По умолчанию оно
истинно, поэтому если при записи нарушена целостность структуры JSON,
то будет сгенерировано исключение и запись выполнена не будет. Но для
увеличения скорости работы эту проверку можно отключить (листинг 1.9).
Листинг 1.9. Отключение проверки структуры записываемого JSON-документа
ЗаписьJSON.ПроверятьСтруктуру = Ложь;

Параметры записи JSON
При записи можно управлять некоторыми параметрами формируемого
текста, например: использованием двойных кавычек, переносом строк,
символами отступа и экранированием символов. Набор параметров записи
задается в объекте ПараметрыЗаписиJSON и затем используется при
открытии для записи JSON-документа.
Перенос строк
По умолчанию при записи JSON используется автоматический перенос
строк. Это удобно для визуального контроля получившегося результата.
Но иногда те или иные HTTP-интерфейсы требуют, чтобы JSON был записан
в одну строку. В таких случаях можно в параметрах записи JSON отключить перенос строк (листинг 1.10). Кроме того, отключение переноса строк
полезно в тех случаях, когда нужно экономить объем передаваемых данных:
если не переносить строки, объем уменьшится в среднем на 20 %.
Листинг 1.10. Отключение переноса строк записываемого JSON-документа




// Задать параметры записи JSON.
ПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет, ″ ″, Истина);
// Создать объект записи и открыть файл, в который будет выполняться запись.
Запись = Новый ЗаписьJSON;
Запись.ОткрытьФайл(″c:\temp\streamWrite_4.json″, , , ПараметрыJSON);`

В результате сформированный JSON-документ будет иметь следующий вид
(листинг 1.11).

Глава 1. Интернет-технологии

19

Листинг 1.11. Содержимое JSON-документа
{″Контрагент1″:{″Код″:″000000017″,″Наименование″:″ОАО Топаз″,″Телефоны″:

[″8-999-777-55-33″,″+71112223344″],″ОбъемПродаж″:5000000,″Поставщик?″:false}}

Символы отступа
По умолчанию перед строками JSON не вставляется синтаксический
отступ. Но для визуального контроля удобно, когда строки форматируются
с помощью синтаксического отступа. Поэтому в примерах выше мы использовали для такого отступа символ табуляции (листинг 1.12).
Листинг 1.12. Набор параметров записи JSON-документа с синтаксическим отступом
ПараметрыJSON = Новый ПараметрыЗаписиJSON(, Символы.Таб);
Запись = Новый ЗаписьJSON;
Запись.ОткрытьФайл(″c:\temp\streamWrite.json″, , , ПараметрыJSON);

Если бы мы этого не сделали (листинг 1.13), результат выглядел бы следующим образом (листинг 1.14).
Листинг 1.13. Запись JSON-документа без синтаксического отступа



Запись.ОткрытьФайл(″c:\temp\streamWrite_5.json″);

Листинг 1.14. Содержимое JSON-документа
{
″Контрагент1″: {
″Код″: ″000000017″,
″Наименование″: ″ОАО Топаз″,
″Телефоны″: [
″8-999-777-55-33″,
″+71112223344″
],
″ОбъемПродаж″: 5000000,
″Поставщик?″: false
}
}

Экранирование символов
Внешние системы могут использовать разные нотации JSON, могут
считать те или иные символы недопустимыми и так далее. Для этих целей
у конструктора ПараметрыЗаписиJSON и у самого объекта есть возможность управлять экранированием таких символов, как амперсанд, одинарные

20

Технологии интеграции «1С:Предприятия 8.3»

кавычки, разделители строк, слеш и угловые скобки. Кроме того, есть
возможность для строковых значений использовать одинарные кавычки
вместо двойных.
Например, прямой слеш при записи в JSON не экранируется по умолчанию
(листинг 1.15).
Листинг 1.15. Запись строки JSON без экранирования символов


Сообщение = Новый СообщениеПользователю;
// Записать строковое значение в JSON.
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
Запись.ЗаписатьЗначение(″путь к файлу C:/файл.txt″);
СтрокаJSON = Запись.Закрыть();
// Показать результат.
Сообщение.Текст = СтрокаJSON;



В результате выполнения приведенного выше фрагмента кода будет получена
следующая строка (листинг 1.16).
Листинг 1.16. Результирующая строка без экранирования слеша
″путь к файлу C:/файл.txt″

Однако эту возможность можно включить (листинг 1.17).
Листинг 1.17. Запись строки JSON с экранированием символов


Сообщение = Новый СообщениеПользователю;
ПараметрыJSON = Новый ПараметрыЗаписиJSON( , , , , , , , ,Истина);
// Записать строковое значение в JSON.
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку(ПараметрыJSON);
Запись.ЗаписатьЗначение(″путь к файлу C:/файл.txt″);
СтрокаJSON = Запись.Закрыть();



// Показать результат.
Сообщение.Текст = СтрокаJSON;
Сообщение.Сообщить();

В результате выполнения приведенного выше фрагмента кода будет получена
следующая строка (листинг 1.18).

Глава 1. Интернет-технологии

21

Листинг 1.18. Результирующая строка с экранированием слеша
″путь к файлу C:\/файл.txt″

Сериализация коллекций значений (объектная техника)
Во многих случаях (например, при обмене информацией с внешними
системами, чтении конфигурационных файлов в формате JSON и др.)
проще и удобнее использовать так называемую объектную технику работы
с JSON с помощью методов глобального контекста ПрочитатьJSON(),
ЗаписатьJSON() и объектов ЧтениеJSON, ЗаписьJSON.
Эта техника позволяет избежать рутинной работы по чтению/записи
каждого отдельного значения или свойства. При чтении документы JSON
отображаются в фиксированный набор типов платформы: Строка, Число,
Булево, Неопределено, Массив, ФиксированныйМассив, Структура,
ФиксированнаяСтруктура, Соответствие, Дата. При записи можно
сформировать в памяти и быстро записать структуру в файл JSON.

Таким образом, объектная техника предполагает достаточно простую работу
с данными, однако платой за это является расход памяти, так как весь JSONдокумент обрабатывается целиком в оперативной памяти. Кроме того,
так можно сериализовать только примитивные типы данных и коллекции
значений. Прикладные типы данных так сериализовать не получится (о сериализации прикладных типов «1С:Предприятия» рассказывается в разделе
«Сериализация прикладных типов "1С:Предприятия"»).
Запись и чтение
Для того чтобы выполнить запись коллекции значений в формате JSON,
нужен сам объект низкоуровневой записи ЗаписьJSON, собственно, записываемая структура данных. И затем все вышеперечисленные объекты
нужно передать в качестве параметров в метод глобального контекста
ЗаписатьJSON().
Рассмотрим следующий пример записи в JSON-документ структуры, состоящей из примитивных типов данных (листинг 1.19).
Листинг 1.19. Пример сериализации структуры в JSON-документ
&НаСервереБезКонтекста
Процедура СериализацияПростыхТиповНаСервере()
// Создать структуру с данными контрагента.
Данные = Новый Структура;
Данные.Вставить(″Контрагент″, ″ОАО Топаз″);
Данные.Вставить(″ОбъемПродаж″, 5000000);

22

Технологии интеграции «1С:Предприятия 8.3»

// Добавить элемент структуры Телефоны типа Массив.
Телефоны = Новый Массив;
Телефоны.Добавить(″+71112223344″);
Телефоны.Добавить(″+79998887766″);
Данные.Вставить(″Телефоны″, Телефоны);
Данные.Вставить(″Поставщик″, Ложь);
// Создать объект записи и открыть файл, в который будет выполняться запись.
Запись = Новый ЗаписьJSON;
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, Символы.Таб);
Запись.ОткрытьФайл(″c:\temp\Serialisation.json″,,, ПараметрыЗаписиJSON);
// Выполнить запись данных (Данные) с помощью объекта записи (Запись).
ЗаписатьJSON(Запись, Данные);
// Завершить работу с файлом.
Запись.Закрыть();
КонецПроцедуры

В результате сформированный JSON-документ будет иметь следующий вид
(листинг 1.20).
Листинг 1.20. Содержимое JSON-документа
{

″Контрагент″: ″ОАО Топаз″,
″ОбъемПродаж″: 5000000,
″Телефоны″: [

″+71112223344″,

″+79998887766″
],
″Поставщик″: false
}

Чтение данных в объектной технике выглядит аналогично записи. Рассмотрим пример чтения JSON-документа, содержимое которого показано выше
(листинг 1.21).
Листинг 1.21. Пример десериализации структуры из JSON-документа
&НаСервереБезКонтекста
Процедура ДесериализацияПростыхТиповНаСервере()
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\Serialisation.json″);
// Выполнить чтение данных в структуру Данные с помощью объекта чтения (Чтение).
Данные = ПрочитатьJSON(Чтение);

Глава 1. Интернет-технологии

23

// Завершить работу с файлом.
Чтение.Закрыть();
// Вывести результат чтения в сообщение.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Контрагент: ″ + Данные.Контрагент + ″, Объем продаж: ″ + Данные.ОбъемПродаж +

″, Поставщик?: ″+ Данные.Поставщик + ″, Телефоны: ″;
// Обойти в цикле элемент структуры данных Телефоны типа Массив.
Для Каждого Телефон Из Данные.Телефоны Цикл

Сообщение.Текст = Сообщение.Текст + Телефон + ″, ″;
КонецЦикла;
Сообщение.Сообщить();
КонецПроцедуры

Результат чтения JSON-документа, содержимое которого показано в листинге
1.20, будет выглядеть следующим образом (рис. 1.2).

Рис. 1.2. Чтение примитивных типов из JSON-документа

Чтение в соответствие
По умолчанию чтение данных методом ПрочитатьJSON() выполняется
в структуру. Но некоторые внешние системы могут формировать имена
свойств, которые, с точки зрения «1С:Предприятия», содержат недопустимые
символы. Например, свойство может называться «user.first» или «user.last».
С точки зрения платформы это недопустимое значение для строкового ключа
(т. к. имя свойства не может содержать точку).
Например, JSON-документ содержит соответствие, имена свойств которого
включают пробел и другие недопустимые символы (листинг 1.22).
Листинг 1.22. Содержимое JSON-документа
{

}

″Контрагент №2″: ″ОАО Алмаз″,
″Объем Продаж″: 6000000,
″Мобильный тел.″: ″+79998887766″,
″Поставщик?″: true

При стандартном чтении такого документа в структуру будет получена
ошибка (рис. 1.3).

24

Технологии интеграции «1С:Предприятия 8.3»

Рис. 1.3. Ошибка при чтении в структуру JSON-документа

В этом случае надо читать данные в соответствие, потому что ключи (имена
свойств) соответствия могут быть любыми. Для этого вторым параметром
в метод ПрочитатьJSON(ЧтениеJSON, Истина) надо передать истину
(листинг 1.23).
Листинг 1.23. Пример десериализации соответствия из JSON-документа
&НаСервереБезКонтекста
Процедура РаботаССоответствиемНаСервере()
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\Serialisation_8.json″);
// Выполнить чтение данных в соответствие Данные с помощью объекта чтения (Чтение).
Данные = ПрочитатьJSON(Чтение, Истина);
// Данные = ПрочитатьJSON(Чтение);
// Завершить работу с файлом.
Чтение.Закрыть();
// Вывести результат чтения в сообщение.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Контрагент: ″ + Данные[″Контрагент №2″]

+ ″, Объем продаж: ″ + Данные[″Объем Продаж″] +

″, Поставщик?: ″ + Данные[″Поставщик?″] + ″, Телефон моб.: ″ + Данные[″Мобильный тел.″];
Сообщение.Сообщить();
КонецПроцедуры

Результат чтения JSON-документа, содержимое которого показано в листинге
1.22, будет выглядеть следующим образом (рис. 1.4).

Рис. 1.4. Чтение данных из JSON-документа в соответствие

Глава 1. Интернет-технологии

25

Функции преобразования и восстановления
Однако на практике далеко не всегда данные могут быть автоматически сериализованы в / десериализованы из JSON. Если данные обладают сложной
структурой или требуют выполнения дополнительных преобразований при
записи или чтении, то можно воспользоваться собственными функциями
преобразования (при записи) или восстановления (при чтении) в/из JSON.
Для этого в методы ЗаписатьJSON() / ПрочитатьJSON() нужно передать
в качестве параметров имя функции преобразования/восстановления, модуль,
в котором эта функция находятся, а также, при необходимости, дополнительные параметры, которые будут переданы в эту функцию.
Необходимо помнить, что функции преобразования/восстановления должны
быть объявлены с указанием ключевого слова Экспорт. Кроме того, если
эти функции находятся в модуле формы, то они могут быть описаны только
в «контекстной» части модуля, то есть с использованием директив компиляции &НаКлиенте или &НаСервере.
Функция преобразования
Функция преобразования, которая используется при сериализации данных
в JSON, будет вызываться для всех свойств, тип которых не поддерживает
преобразование в формат JSON напрямую. В этой функции разработчик
может самостоятельно проанализировать переданный объект и вернуть платформе значение, которое может быть сериализовано в JSON, либо вообще
отказаться от его записи.
Например, структура с информацией о контрагенте помимо примитивных
типов данных содержит объект встроенного языка ДанныеАдреса. JSON
ничего не знает о подобных типах, поэтому если попытаться записать данные
в JSON обычным образом, без использования функции преобразования, то
будет получена ошибка из-за недопустимых для сериализации типов данных.
Поэтому в метод ЗаписатьJSON() мы передаем имя функции преобразования и указываем, что она находится в том же модуле формы (ЭтотОбъект),
что и процедура сериализации данных (листинг 1.24).
Листинг 1.24. Примерсериализации в JSON-документ
с использованием функции преобразования
&НаСервере
Процедура СериализацияСФункциейПреобразованияНаСервере()
// Создать структуру с данными контрагента.
Данные = Новый Структура;
Данные.Вставить(″Контрагент″, ″ОАО Топаз″);
Данные.Вставить(″ОбъемПродаж″, 5000000);

26

Технологии интеграции «1С:Предприятия 8.3»

// Записать свойство Адрес, имеющее тип ДанныеАдреса.
ДопИнфо = Новый ДанныеАдреса(Новый Структура(

″Город, Страна, Индекс″, ″Москва″, ″Россия″, ″112233″));
Данные.Вставить(″Адрес″, ДопИнфо);
Данные.Вставить(″Поставщик″, Ложь);
// Создать объект записи и открыть файл, в который будет выполняться запись.
Запись = Новый ЗаписьJSON;
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, Символы.Таб);
Запись.ОткрытьФайл(″c:\temp\Serialisation_4.json″,,, ПараметрыЗаписиJSON);
// Выполнить запись данных (Данные) с помощью объекта записи (Запись).
ЗаписатьJSON(Запись, Данные, , ″ФункцияПреобразованияЗаписи″, ЭтотОбъект);
// Завершить работу с файлом.
Запись.Закрыть();
КонецПроцедуры

Отличие от обычной записи данных в JSON-документ заключается
в том, что для всех свойств, тип которых не поддерживает автоматическую
сериализацию в JSON, вызывается функция преобразования ФункцияПреобразованияЗаписи().

В этой функции в параметрах Свойство и Значение содержатся соответственно имя и значение того поля структуры данных, которое не может быть
автоматически сериализовано в JSON (листинг 1.25).
Листинг 1.25. Функция преобразования
&НаСервере
Функция ФункцияПреобразованияЗаписи(Свойство, Значение, ДополнительныеПараметры, Отказ) Экспорт
Если Свойство = ″Адрес″ Тогда

Адрес = ″Страна: ″ + Значение.Страна + ″, Индекс: ″ + Значение.Индекс + ″

, Город: ″ + Значение.Город;

Возврат Адрес;
КонецЕсли;
Отказ = Истина;
КонецФункции

В функции преобразования мы извлекаем данные из объекта ДанныеАдреса в строку, и эта строка возвращается в метод ЗаписатьJSON().

В результате сформированный JSON-документ будет иметь следующий вид
(листинг 1.26).

Глава 1. Интернет-технологии

27

Листинг 1.26. Содержимое JSON-документа
{

}

″Контрагент″: ″ОАО Топаз″,
″ОбъемПродаж″: 5000000,
″Адрес″: ″Страна: Россия, Индекс: 112233, Город: Москва″,
″Поставщик″: false

Функция восстановления
При чтении из JSON функция восстановления может использоваться для
того, чтобы преобразовать данные JSON в типы «1С:Предприятия», которые
не могут являться результатом автоматического преобразования, либо чтобы
просто изменить получаемые данные, если есть такая необходимость.
При использовании функции восстановления разработчик должен знать,
какие данные и в каком виде находятся JSON-документе.
Например, требуется прочитать из JSON-документа, показанного в листинге
1.27, информацию о контрагенте и при этом представить телефоны контрагента в привычном формате: «999-99-99».
Листинг 1.27. Содержимое JSON-документа
{

″Контрагент″: ″ОАО Топаз″,
″ОбъемПродаж″: 5000000,
″Телефоны″: [

″+71112223344″,

″+79998887766″
],
″Поставщик″: false
}

Для этого в метод ПрочитатьJSON() мы передаем имя функции восстановления и указываем, что она находится в том же модуле формы (ЭтотОбъект),
что и процедура десериализации (листинг 1.28).
Листинг 1.28. Пример десериализации данных из JSON-документа
с функцией восстановления
&НаСервере
Процедура ДесериализацияСФункциейВосстановленияНаСервере()
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\Serialisation_6.json″);
// Выполнить чтение данных в структуру Данные с помощью объекта чтения (Чтение).

28

Технологии интеграции «1С:Предприятия 8.3»
Данные = ПрочитатьJSON(Чтение, Ложь, , , ″ФункцияВосстановленияЧтения″, ЭтотОбъект);
// Завершить работу с файлом.
Чтение.Закрыть();

// Вывести результат чтения в сообщение.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Контрагент: ″ + Данные.Контрагент + ″, Объем продаж: ″ + Данные.ОбъемПродаж +

″, Поставщик?: ″+ Данные.Поставщик + ″, Телефоны: ″;
Для Каждого Телефон Из Данные.Телефоны Цикл

Сообщение.Текст = Сообщение.Текст + Телефон + ″, ″;
КонецЦикла;
Сообщение.Сообщить();
КонецПроцедуры

Отличие от обычного чтения из JSON-документа заключается в том, что
при чтении вызывается функция восстановления ФункцияВосстановленияЧтения(). Преобразованные с помощью функции восстановления
данные сохраняются в структуру Данные. После этого структура данных
выводится в сообщение пользователю.
В функции восстановления мы обходим массив телефонов и представляем
каждый телефон в привычном виде. Результат восстановления возвращается
в метод ПрочитатьJSON() (листинг 1.29).
Листинг 1.29. Функция восстановления
&НаСервере
Функция ФункцияВосстановленияЧтения(Свойство, Значение, ДополнительныеПараметры) Экспорт
Если Свойство = ″Телефоны″ И ТипЗнч(Значение) = Тип(″Массив″) Тогда

Телефоны = Новый Массив;

Для Каждого Телефон Из Значение Цикл

СтрокаТелефона = Лев(Телефон,2) + ″-″ + Сред(Телефон, 3, 3)

+ ″-″ + Сред(Телефон, 6, 3) + ″-″ +

″-″ + Сред(Телефон, 9, 2) + ″-″ + Прав(Телефон,2);

Телефоны.Добавить(СтрокаТелефона);

КонецЦикла;

Возврат Телефоны;
КонецЕсли;
КонецФункции

Результат чтения JSON-документа, содержимое которого показано в листинге
1.27, будет выглядеть следующим образом (рис. 1.5).

Рис. 1.5. Чтение из JSON-документа с функцией восстановления

Глава 1. Интернет-технологии

29

Однако необходимо учитывать, что функция восстановления будет вызвана
для всех свойств, которые будут обнаружены в JSON-документе. Это
не всегда удобно и, кроме того, существенно снижает производительность
чтения JSON-документа, особенно если он большой.
Для того чтобы ускорить чтение JSON, можно передать в метод
ПрочитатьJSON() массив имен свойств, для которых требуется вызывать
функцию восстановления (листинг 1.30).
Листинг 1.30. Пример десериализации данных из JSON-документа
с функцией восстановления
&НаСервере
Процедура ДесериализацияСФункциейВосстановленияНаСервере()
// Заполнить массив имен реквизитов для восстановления.
РеквизитыДляВосстановления = Новый Массив;
РеквизитыДляВосстановления.Добавить(″Телефоны″);
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\Serialisation_6.json″);
// Выполнить чтение данных в структуру Данные с помощью объекта чтения (Чтение).
Данные = ПрочитатьJSON(Чтение, Ложь, , , ″ФункцияВосстановленияЧтения″

, ЭтотОбъект, , РеквизитыДляВосстановления);
// Завершить работу с файлом.
Чтение.Закрыть();
// Вывести результат чтения в сообщение.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Контрагент: ″ + Данные.Контрагент + ″, Объем продаж: ″ + Данные.ОбъемПродаж +

″, Поставщик?: ″+ Данные.Поставщик + ″, Телефоны: ″;
Для Каждого Телефон Из Данные.Телефоны Цикл

Сообщение.Текст = Сообщение.Текст + Телефон + ″, ″;
КонецЦикла;
Сообщение.Сообщить();
КонецПроцедуры

В результате чтение данных из JSON-документа будет выполнено быстрее,
так как функция восстановления будет вызвана только для свойств, имена
которых перечислены в массиве РеквизитыДляВосстановления.
Сериализация типа «Дата»
JSON не стандартизует формат представления даты. Поэтому разработчик
может самостоятельно определить формат представления даты в JSONдокументе исходя из своих предпочтений и требований принимающей
стороны.

30

Технологии интеграции «1С:Предприятия 8.3»

«1С:Предприятие» поддерживает несколько форматов представления
даты, которые можно задать с помощью системного перечисления
ФорматДатыJSON:
■■ Формат ISO. В этом случае дата сериализуется следующим образом:
«2019-03-15T00:00:00+0400».
■■ Формат JavaScript. В этом случае дата сериализуется следующим образом:
«new Date(1234656000000)».
■■ Формат Microsoft. В этом случае дата сериализуется следующим образом:
«\/Date(1234656000000)\/» или «/Date(1234656000000)/» (в зависимости
от режима экранирования символов).
Дата может записываться в нескольких вариантах (для примера используется
дата 10 марта 2019 13:14:15 в зоне UTC+4):
■■ как локальная дата: 2019-03-10T13:14:15;
■■ как локальная дата с указанием смещения: 2019-03-10T13:14:15+04:00;
■■ как дата в UTC: 2019-03-10T09:14:15Z.
Управлять этим можно с помощью системного перечисления
ВариантЗаписиДатыJSON. Дату в варианте UTC можно записать в любом
формате (ISO, JavaScript и Microsoft), остальные варианты представления
даты возможны только в том случае, если сериализация выполняется
в формате ISO.
Для установки желаемого формата и варианта записи даты в JSON-документ
у метода ЗаписатьJSON() можно использовать параметр НастройкиСериализации.
При чтении даты из JSON-документа нужно учитывать, что значения даты
представляются в нем строкой. В параметре ИменаСвойствСоЗначениямиДата метода ПрочитатьJSON() можно перечислить те свойства
JSON, значения которых нужно преобразовать в дату «1С:Предприятия».
А в параметре ОжидаемыйФорматДаты можно указать, в каком формате
эти данные содержатся в JSON (ISO, JavaScript или Microsoft). Однако если
окажется, что в какой-то момент формат данных JSON не совпадает с ожидаемым форматом, то будет вызвано исключение.
Поясним вышесказанное на примерах. Сначала попробуем записать значение
даты обычным образом (листинг 1.31).

Глава 1. Интернет-технологии

31

Листинг 1.31. Пример сериализации даты в JSON-документ
&НаСервереБезКонтекста
Процедура СериализацияТипаДатаНаСервере()
// Создать структуру с данными контрагента.
Данные = Новый Структура;
Данные.Вставить(″Контрагент″, ″ОАО Фонтан″);
Данные.Вставить(″Телефон″, ″81234567788″);
Данные.Вставить(″ДатаИзменения″, ТекущаяДата());
// Создать объект записи и открыть файл, в который будет выполняться запись.
Запись = Новый ЗаписьJSON;
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, Символы.Таб);
Запись.ОткрытьФайл(″c:\temp\Serialisation_2.json″,,, ПараметрыЗаписиJSON);
// Выполнить запись данных (Данные) с помощью объекта записи (Запись).
ЗаписатьJSON(Запись, Данные);
// Завершить работу с файлом.
Запись.Закрыть();
КонецПроцедуры

В результате сформированный JSON-документ будет иметь следующий вид
(листинг 1.32).
Листинг 1.32. Содержимое JSON-документа
{

}

″Контрагент″: ″ОАО Фонтан″,
″Телефон″: ″81234567788″,
″ДатаИзменения″: ″2019-02-28T20:48:54″

То есть стандартно дата сериализуется как локальная дата в формате ISO.
Прочитать такую дату можно следующим образом (листинг 1.33).
Листинг 1.33. Пример десериализации даты из JSON-документа
&НаСервереБезКонтекста
Процедура ДесериализацияТипаДатаНаСервере()
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\Serialisation_2.json″);
// Выполнить чтение данных в структуру Данные с помощью объекта чтения (Чтение).
Данные = ПрочитатьJSON(Чтение, Ложь, ″ДатаИзменения″, ФорматДатыJSON.ISO);
// Завершить работу с файлом.
Чтение.Закрыть();

32

Технологии интеграции «1С:Предприятия 8.3»

// Вывести результат чтения в сообщение.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Контрагент: ″ + Данные.Контрагент + ″, Телефон: ″ + Данные.Телефон +

″, Дата изменения: ″+ Данные.ДатаИзменения;
Сообщение.Сообщить();
КонецПроцедуры

В процедуре десериализации при вызове метода ПрочитатьJSON() мы
указываем имя свойства, содержащее дату («ДатаИзменения»), и ожидаемый
формат даты – значение ISO перечисления ФорматДатыJSON.
Результат чтения JSON-документа, содержимое которого показано в листинге
1.32, будет выглядеть следующим образом (рис. 1.6).

Рис. 1.6. Чтение даты из JSON-документа

Однако

если

указать

другой

ожидаемый

формат

даты,

например

ФорматДатыJSON.JavaScript, то будет получена ошибка из-за неверного

формата даты.

Чтобы этого избежать, можно считывать данные обычным образом,
а затем использовать так называемую постобработку при помощи функции
ПрочитатьДатуJSON() (листинг 1.34).
Листинг 1.34. Пример десериализации даты из JSON-документа
&НаСервереБезКонтекста
Процедура ДесериализацияТипаДатаНаСервере()
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\Serialisation_2.json″);
// Выполнить чтение данных в структуру Данные с помощью объекта чтения (Чтение).
Данные = ПрочитатьJSON(Чтение);
// Завершить работу с файлом.
Чтение.Закрыть();
// Прочитать дату JSON в нужном формате.
ДатаИзменения = ПрочитатьДатуJSON(Формат(Данные.ДатаИзменения, ″ЧГ=″)
, ФорматДатыJSON.ISO);
// Вывести результат чтения в сообщение.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Контрагент: ″ + Данные.Контрагент + ″, Телефон: ″ + Данные.Телефон +

″, Дата изменения: ″ + ДатаИзменения;
Сообщение.Сообщить();
КонецПроцедуры

Глава 1. Интернет-технологии

33

Результат чтения будет аналогичен показанному на рис. 1.6.
Теперь укажем формат и вариант записи даты при помощи параметра
НастройкиСериализации (листинг 1.35).
Листинг 1.35. Пример сериализации даты в JSON-документ
&НаСервереБезКонтекста
Процедура СериализацияТипаДатаНаСервере()
// Создать структуру с данными контрагента.
Данные = Новый Структура;
Данные.Вставить(″Контрагент″, ″ОАО Фонтан″);
Данные.Вставить(″Телефон″, ″81234567788″);
Данные.Вставить(″ДатаИзменения″, ТекущаяДата());
// Создать настройки сериализации для записи дат.
НастройкиСериализации = Новый НастройкиСериализацииJSON;
НастройкиСериализации.ФорматСериализацииДаты = ФорматДатыJSON.JavaScript;
НастройкиСериализации.ВариантЗаписиДаты
= ВариантЗаписиДатыJSON.УниверсальнаяДата;
// Создать объект записи и открыть файл, в который будет выполняться запись.
Запись = Новый ЗаписьJSON;
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, Символы.Таб);
Запись.ОткрытьФайл(″c:\temp\Serialisation_3.json″,,, ПараметрыЗаписиJSON);
// Выполнить запись данных (Данные) с помощью объекта записи (Запись).
ЗаписатьJSON(Запись, Данные, НастройкиСериализации);
// Завершить работу с файлом.
Запись.Закрыть();
КонецПроцедуры

В результате сформированный JSON-документ будет иметь следующий вид
(листинг 1.36).
Листинг 1.36. Содержимое JSON-документа
{

}

″Контрагент″: ″ОАО Фонтан″,
″Телефон″: ″81234567788″,
″ДатаИзменения″: ″new Date(1551382927000)″

В показанном примере дата сериализуется как универсальная дата в формате
JavaScript. Прочитать такую дату можно следующим образом (листинг 1.37).

34

Технологии интеграции «1С:Предприятия 8.3»

Листинг 1.37. Пример десериализации даты из JSON-документа
&НаСервереБезКонтекста
Процедура ДесериализацияТипаДатаНаСервере()
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\Serialisation_3.json″);
// Выполнить чтение данных в структуру Данные с помощью объекта чтения (Чтение).
Данные = ПрочитатьJSON(Чтение, Ложь, ″ДатаИзменения″, ФорматДатыJSON.JavaScript);
// Завершить работу с файлом.
Чтение.Закрыть();
// Вывести результат чтения в сообщение.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Контрагент: ″ + Данные.Контрагент + ″, Телефон: ″ + Данные.Телефон +

″, Дата изменения: ″+ Данные.ДатаИзменения;
Сообщение.Сообщить();
КонецПроцедуры

В процедуре десериализации при вызове метода ПрочитатьJSON() мы
указываем имя свойства, содержащее дату («ДатаИзменения»), и ожидаемый
формат даты – значение JavaScript перечисления ФорматДатыJSON.
Результат чтения будет аналогичен показанному на рис. 1.6.
Этого же результата можно добиться, выполнив постобработку при помощи
функции ПрочитатьДатуJSON() (листинг 1.38).
Листинг 1.38. Пример десериализации даты из JSON-документа
&НаСервереБезКонтекста
Процедура ДесериализацияТипаДатаНаСервере()
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\Serialisation_2.json″);
// Выполнить чтение данных в структуру Данные с помощью объекта чтения (Чтение).
Данные = ПрочитатьJSON(Чтение);
// Завершить работу с файлом.
Чтение.Закрыть();
// Прочитать дату JSON в нужном формате.
ДатаИзменения = ПрочитатьДатуJSON(Формат(Данные.ДатаИзменения, ″ЧГ=″)
, ФорматДатыJSON.JavaScript);
// Вывести результат чтения в сообщение.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Контрагент: ″ + Данные.Контрагент + ″, Телефон: ″ + Данные.Телефон +

″, Дата изменения: ″ + ДатаИзменения;
Сообщение.Сообщить();
КонецПроцедуры

Глава 1. Интернет-технологии

35

Вместо постобработки данных в процедуре чтения лучше и методологически
более правильно использовать чтение с функцией восстановления.
Например, дата в JSON-документе может быть представлена в виде числа
(листинг 1.39).
Листинг 1.39. Содержимое JSON-документа
{

}

″Контрагент″: ″ОАО Фонтан″,
″Телефон″: ″81234567788″,
″ДатаИзменения″: 63661814555

Чтобы прочитать такую дату, можно использовать чтение с функцией восстановления (листинг 1.40).
Листинг 1.40. Пример десериализации даты из JSON-документа
с функцией восстановления
&НаСервереБезКонтекста
Процедура ДесериализацияТипаДатаНаСервере()
// Заполнить массив имен реквизитов для восстановления.
РеквизитыДляВосстановления = Новый Массив;
РеквизитыДляВосстановления.Добавить(″Телефон″);
РеквизитыДляВосстановления.Добавить(″ДатаИзменения″);
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\Serialisation_7.json″);
// Выполнить чтение данных в структуру Данные с помощью объекта чтения (Чтение).
Данные = ПрочитатьJSON(Чтение, Ложь, , , ″ФункцияВосстановленияЧтения″
, ЭтотОбъект, , РеквизитыДляВосстановления);
// Завершить работу с файлом.
Чтение.Закрыть();
// Вывести результат чтения в сообщение.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Контрагент: ″ + Данные.Контрагент + ″, Телефон: ″ + Данные.Телефон +

″, Дата изменения: ″+ Данные.ДатаИзменения;
Сообщение.Сообщить();
КонецПроцедуры

В функции восстановления дата, выраженная простым числом, с помощью
функции ПрочитатьДатуJSON() приводится к формату JavaScript (листинг
1.41).

36

Технологии интеграции «1С:Предприятия 8.3»

Листинг 1.41. Функция восстановления
&НаСервере
Функция ФункцияВосстановленияЧтения(Свойство, Значение, ДополнительныеПараметры) Экспорт
Если Свойство = ″ДатаИзменения″ Тогда
Возврат ПрочитатьДатуJSON(″new Date(″ + Формат(Значение, ″ЧГ=″)+″)″
, ФорматДатыJSON.JavaScript);
КонецЕсли;
Если Свойство = ″Телефон″ Тогда

СтрокаТелефона = Лев(Значение,1) + ″ ″ + Сред(Значение, 2, 3) + ″-″ + Сред(Значение, 5, 3) + ″-″ +

Сред(Значение, 8, 2) + ″-″ + Прав(Значение,2);

Возврат СтрокаТелефона;
КонецЕсли;
КонецФункции

Результат чтения JSON-документа, содержимое которого показано в листинге
1.39, будет выглядеть следующим образом (рис. 1.7).

Рис. 1.7. Чтение даты из JSON-документа с функцией восстановления

При сериализации/десериализации даты с использованием потоковой
техники также можно использовать методы глобального контекста
ЗаписатьДатуJSON() / ПрочитатьДатуJSON().

Например, в следующем фрагменте потоковой записи дата сериализуется как
универсальная дата в формате ISO (листинг 1.42).
Листинг 1.42. Сериализация даты в JSON-документ с использованием потоковой
техники




Запись.ЗаписатьИмяСвойства(″ДатаИзменения″);
Запись.ЗаписатьЗначение(ЗаписатьДатуJSON(ТекущаяДата(), ФорматДатыJSON.ISO
, ВариантЗаписиДатыJSON.УниверсальнаяДата));

В результате в сформированном JSON-документе значение даты будет выглядеть следующим образом (листинг 1.43).
Листинг 1.43. Фрагмент сформированного JSON-документа



″ДатаИзменения″: ″2019-03-22T12:50:16Z″

Глава 1. Интернет-технологии

37

Прочитать такую дату можно следующим образом (листинг 1.44).
Листинг 1.44. Десериализация даты из JSON-документа с использованием потоковой
техники


Если Чтение.ТекущееЗначение = ″ДатаИзменения″ Тогда

Чтение.Прочитать();

Сообщение.Текст = ″Значение = ″ +
ПрочитатьДатуJSON(Формат(Чтение.ТекущееЗначение, ″ЧГ=″)
, ФорматДатыJSON.ISO);

Сообщение.Сообщить();
КонецЕсли;


Результат чтения JSON-документа, фрагмент которого показан в листинге
1.43, будет выглядеть следующим образом (рис. 1.8).

Рис. 1.8. Потоковое чтение даты из JSON-документа

Сериализация прикладных типов «1С:Предприятия»
Помимо потоковой записи/чтения и сериализации примитивных типов
и коллекций в платформе разработаны средства, позволяющие сериализовать/десериализовать прикладные типы «1С:Предприятия»: ссылки, объекты,
наборы записей и вообще любые типы, для которых поддерживается XDTOсериализация.
В основном XDTO-сериализацию в JSON рекомендуется использовать при
обмене данными между двумя прикладными решениями «1С:Предприятия».
Преимущества обмена через формат JSON состоят в том, что по сравнению
с обменом через XML этот обмен работает быстрее, а сами файлы обмена
в формате JSON записываются более компактно.
Кроме того, этот механизм можно использовать и для обмена с внешними
системами, готовыми принимать типы данных «1С:Предприятия». Например,
XDTO-сериализацию в JSON можно использовать для организации собственного HTTP-интерфейса прикладного решения. Сервис на платформе
«1С:Предприятия» будет формировать ответ в памяти в виде строки JSON.
А затем передавать ее при помощи объекта HTTPСервисОтвет. Подробнее
об этом будет рассказано в разделе «HTTP-запросы».

38

Технологии интеграции «1С:Предприятия 8.3»

Запись
JSON-сериализация «эмулирует» XML-сериализацию, в силу чего получающийся JSON-документ внешне выглядит очень похоже на соответствующий
XML-документ. В JSON-документ могут быть помещены любые объекты
системы «1С:Предприятие», для которых указано, что они могут быть сериализованы в XDTO.
Общие принципы сериализации в JSON идентичны XDTO-сериализации
в XML. В частности:
■■ структура данных соответствует структуре XML-документа;
■■ имеются незначительные отличия, связанные с особенностями хранения
типов и представлением массивов в JSON;
■■ порядок и состав свойств JSON соответствует структуре объекта конфигурации и не может изменяться.
Сериализация прикладных типов в JSON-документ происходит
с помощью метода ЗаписатьJSON() объекта глобального контекста
СериализаторXDTO.
При этом существует одна важная особенность. В отличие от XML для
записи начала объекта JSON нет необходимости указывать его имя. Поэтому
сериализация/десериализация в/из JSON может выполняться одним из двух
способов.

Во-первых, при сериализации в параметре НазначениеТипаXML
метода ЗаписатьJSON() можно указать явное назначение типа
(НазначениеТипаXML.Явное). Десериализовать такой объект можно без
указания типа считываемого значения (листинг 1.45).
Листинг 1.45. Пример сериализации элемента справочника
в JSON-документ с указанием типа
&НаСервереБезКонтекста
Процедура СериализацияПрикладныхТиповНаСервере()
// Получить записываемый объект.
СсылкаНаОбъект = Справочники.Сотрудники.НайтиПоКоду(″000000003″);
Объект = СсылкаНаОбъект.ПолучитьОбъект();
// Создать объект записи и открыть файл, в который будет выполняться запись.
Запись = Новый ЗаписьJSON;
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, Символы.Таб);
Запись.ОткрытьФайл(″c:\temp\SerialisationXDTO.json″,,, ПараметрыЗаписиJSON);
// Выполнить запись данных (Объект) с помощью объекта записи (Запись).
СериализаторXDTO.ЗаписатьJSON(Запись, Объект, НазначениеТипаXML.Явное);
// Завершить работу с файлом.
Запись.Закрыть();
КонецПроцедуры

Глава 1. Интернет-технологии

39

В процедуре записи ссылка на объект справочника Сотрудники находится по
коду, а затем данные объекта записываются в JSON-документ с указанием его
типа.
В результате сформированный JSON-документ будет иметь следующий вид
(листинг 1.46).
Листинг 1.46. Содержимое JSON-документа
{

″#type″: ″jcfg:CatalogObject.Сотрудники″,
″#value″: {

″Ref″: ″0b6cd7dc-36a0-11e9-8bb2-642737df2048″,

″DeletionMark″: false,

″Code″: ″000000003″,

″Description″: ″Артемов Игорь Владимирович″,

″ДатаРождения″: ″1970-01-31T00:00:00″,

″КоличествоДетей″: 2,

″Работает″: true,

″Стаж″: ″25.03.20″
}
}

Из соображений компактности сформированного JSON-документа для встроенных типов платформы пространства имен не записываются полностью
в отдельном свойстве JSON, а указываются в виде префикса перед типом,
например jcfg (см. листинг 1.46).
В данном случае префикс cfg обозначает пространство имен http://v8.1c.ru/8.1/
data/enterprise/current-config. Соответствие префиксов пространствам имен
«зашито» в платформу. Поэтому обмен данными между прикладными решениями «1С:Предприятия» выполняется стандартным образом. А при обмене
с внешними системами для правильной десериализации типов платформы
внешней системе понадобится полная таблица соответствия, приведенная
в документации.
Во-вторых, можно выполнить сериализацию в JSON без явного назначения
типа (листинг 1.47). Но тогда тип нужно будет указать при десериализации
(см. листинг 1.50).
Листинг 1.47. Пример сериализации элемента справочника
в JSON-документ без указания типа
&НаСервереБезКонтекста
Процедура СериализацияПрикладныхТиповНаСервере()
// Получить записываемый объект.
СсылкаНаОбъект = Справочники.Сотрудники.НайтиПоКоду(″000000002″);
Объект = СсылкаНаОбъект.ПолучитьОбъект();

40

Технологии интеграции «1С:Предприятия 8.3»
// Создать объект записи и открыть файл, в который будет выполняться запись.
Запись = Новый ЗаписьJSON;
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, Символы.Таб);
Запись.ОткрытьФайл(″c:\temp\SerialisationXDTO_2.json″,,, ПараметрыЗаписиJSON);
// Выполнить запись данных (Объект) с помощью объекта записи (Запись).
СериализаторXDTO.ЗаписатьJSON(Запись, Объект);
// Завершить работу с файлом.
Запись.Закрыть();

КонецПроцедуры

В результате сформированный JSON-документ будет иметь следующий вид
(листинг 1.48).
Листинг 1.48. Содержимое JSON-документа
{

″#value″: {

″Ref″: ″0b6cd7db-36a0-11e9-8bb2-642737df2048″,

″DeletionMark″: false,

″Code″: ″000000002″,

″Description″: ″Смирнова Светлана Ивановна″,

″ДатаРождения″: ″1990-02-22T00:00:00″,

″КоличествоДетей″: 0,

″Работает″: false,

″Стаж″: ″03.10.27″
}
}

Кроме того, нужно учитывать, что сериализация значений типа Дата выполняется в формате ISO. Это определяется механизмом XDTO и не управляется
при записи данных. Также при операции сериализации не поддерживается
использование функции преобразования, в отличие от потоковой и объектной
техник.
Чтение
Общие принципы десериализации также связаны с использованием механизма XDTO. В частности:
■■ десериализуются только те типы, которые поддерживают XDTOсериализацию;
■■ порядок свойств JSON должен соответствовать структуре объекта конфигурации.
Десериализация прикладных типов из JSON-документа происходит
с помощью метода ПрочитатьJSON() объекта глобального контекста
СериализаторXDTO. При этом может быть либо указан тип считываемого

Глава 1. Интернет-технологии

41

значения, либо нет – в зависимости от того, указан ли тип прикладного
объекта при сериализации.
Например, чтение JSON-документа, содержимое которого показано
в листинге 1.46, может быть выполнено без указания типа считываемого
значения (листинг 1.49).
Листинг 1.49. Пример десериализации элемента справочника
из JSON-документа без указания типа
&НаСервереБезКонтекста
Процедура ДесериализацияПрикладныхТиповНаСервере()
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\SerialisationXDTO.json″);
// Выполнить чтение данных в структуру Данные с помощью объекта чтения (Чтение).
Данные = СериализаторXDTO.ПрочитатьJSON(Чтение);
// Завершить работу с файлом.
Чтение.Закрыть();
// Вывести результат чтения в сообщение.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Код: ″ + Данные.Code + ″, ФИО: ″ + Данные.Description + ″, ДР:

″+ Данные.ДатаРождения +

″, Детей: ″ + Данные.КоличествоДетей + ″, Работает: ″ + Данные.Работает + ″

, Стаж: ″ + Данные.Стаж;
Сообщение.Сообщить();
КонецПроцедуры

Результат чтения элемента справочника Сотрудники из JSON-документа будет
выглядеть следующим образом (рис. 1.9).

Рис. 1.9. Чтение элемента справочника из JSON-документа

Если же тип прикладного объекта не указан при сериализации (см. листинги
1.47, 1.48), то при чтении JSON-документа тип считываемого значения должен
быть указан в методе ПрочитатьJSON() объекта СериализаторXDTO
(листинг 1.50).

42

Технологии интеграции «1С:Предприятия 8.3»

Листинг 1.50. Пример десериализации элемента справочника
из JSON-документа с указанием типа
&НаСервереБезКонтекста
Процедура ДесериализацияПрикладныхТиповНаСервере()
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\SerialisationXDTO_2.json″);
// Выполнить чтение данных в структуру Данные с помощью объекта чтения (Чтение).
Данные = СериализаторXDTO.ПрочитатьJSON(Чтение, Тип(″СправочникОбъект.Сотрудники″));
// Завершить работу с файлом.
Чтение.Закрыть();
// Вывести результат чтения в сообщение.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Код: ″ + Данные.Code + ″, ФИО: ″ + Данные.Description

+ ″, ДР: ″+ Данные.ДатаРождения +

″, Детей: ″ + Данные.КоличествоДетей + ″, Работает: ″ + Данные.Работает

+ ″, Стаж: ″ + Данные.Стаж;
Сообщение.Сообщить();
КонецПроцедуры

Десериализация прикладных типов
с помощью функции восстановления
При десериализации, так же как и в случае с примитивными типами
и коллекциями, есть возможность изменить получаемые данные. Для этого
можно использовать функцию восстановления.
Например, при переносе элемента справочники Сотрудники из одной базы
в другую требуется каким-то образом изменить его код. Пусть сформированный JSON-документ имеет следующий вид (листинг 1.51).
Листинг 1.51. Содержимое JSON-документа
{

″#type″: ″jcfg:CatalogObject.Сотрудники″,
″#value″: {

″Ref″: ″9575f8ed-369f-11e9-8bb2-642737df2048″,

″DeletionMark″: false,

″Code″: ″000000001″,

″Description″: ″Алексеев Сергей Иванович″,

″ДатаРождения″: ″1980-12-10T00:00:00″,

″КоличествоДетей″: 1,

″Работает″: true,

″Стаж″: ″10.05.01″,

″Должность″: ″88d54406-36a1-11e9-8bb2-642737df2048″
}
}

Глава 1. Интернет-технологии

43

Для этого в метод ПрочитатьJSON() мы передаем имя функции восстановления и массив имен свойств, для которых она должна вызываться (листинг
1.52).
Листинг 1.52. Пример десериализации элемента справочника
с функцией восстановления
&НаСервере
Процедура ВосстановлениеПриДесериализацииХДТОНаСервере()
// Создать объект чтения и открыть файл, из которого будет выполняться чтение.
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(″c:\temp\SerialisationXDTO_3.json″);
// Заполнить массив имен реквизитов для восстановления.
РеквизитыДляВосстановления = Новый Массив;
РеквизитыДляВосстановления.Добавить(″Code″);
// Выполнить чтение данных в структуру Данные с помощью объекта чтения (Чтение).
Данные = СериализаторXDTO.ПрочитатьJSON(Чтение, , ″ФункцияВосстановленияЧтенияXDTO″

, ЭтотОбъект, , , РеквизитыДляВосстановления);
// Завершить работу с файлом.
Чтение.Закрыть();
// Вывести результат чтения в сообщение.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Код: ″ + Данные.Code + ″, ФИО: ″ + Данные.Description

+ ″, ДР: ″+ Данные.ДатаРождения +

″, Детей: ″ + Данные.КоличествоДетей + ″, Работает: ″ + Данные.Работает

+ ″, Стаж: ″ + Данные.Стаж +

″, Должность: ″ + Данные.Должность;
Сообщение.Сообщить();
КонецПроцедуры

Функцию восстановления заполним следующим образом (листинг 1.53).
Листинг 1.53. Функция восстановления
&НаСервере
Функция ФункцияВосстановленияЧтенияXDTO(Свойство, Тип, Значение, ДополнительныеПараметры) Экспорт
Если Свойство = ″Code″ Тогда

КодОбмена = ″НМК-″ + Прав(Значение.Значение, 5);

ТипXDTOКод = ФабрикаXDTO.Тип(″http://www.w3.org/2001/XMLSchema″,″string″);

Возврат ФабрикаXDTO.Создать(ТипXDTOКод, КодОбмена);
КонецЕсли;
КонецФункции

44

Технологии интеграции «1С:Предприятия 8.3»

В функции восстановления в параметре Значение будет содержаться объект
XDTO. Его значение (Значение.Значение) мы изменяем нужным образом
и возвращаем в метод ПрочитатьJSON().
Результат чтения JSON-документа, содержимое которого показано в листинге
1.51, будет выглядеть следующим образом (рис. 1.10).

Рис. 1.10. Чтение элемента справочника из JSON-документа с функцией восстановления

Смешанная техника работы
Для упрощения работы с JSON можно совмещать различные техники при
формировании одного документа. Например, необходимо сформировать
документ, который содержит некоторый набор структур и массив. В этом
случае можно все оформление документа выполнять с помощью потоковой
техники, а уже готовые структуры и массив записывать с помощью объектной
техники.
Например, нам нужно записать в JSON-документ список оплат поставщикам
за поступившие товары. Этот список должен содержать дату формирования
и набор оплат (соответствующий некоторому критерию).
Каждая оплата поставщику характеризуется следующими параметрами:
■■
■■
■■
■■
■■

номер оплаты;
дата совершения оплаты;
поставщик по оплате;
сумма оплаты;
ссылка на оплату, по которой впоследствии можно получить всю информацию по оплате поставщику.
Процедура, которая формирует JSON-документ, может иметь следующий вид
(листинг 1.54).
Листинг 1.54. Пример сериализации в JSON-документ
с применением смешанной техники
&НаСервереБезКонтекста
Процедура СовмещениеТехникНаСервере()
СписокОплат = Документы.ОплатаПоставщикам.Выбрать();
// Создать настройки сериализации для записи дат.

Глава 1. Интернет-технологии

45

НастройкиСериализации = Новый НастройкиСериализацииJSON;
НастройкиСериализации.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.УниверсальнаяДата;
НастройкиСериализации.ФорматСериализацииДаты = ФорматДатыJSON.ISO;
НастройкиСериализации.СериализовыватьМассивыКакОбъекты = Истина;
// Задать параметры записи JSON.
ПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, ″ ″, Истина);
// Создать объект записи и открыть файл, в который будет выполняться запись.
Запись = Новый ЗаписьJSON;
Запись.ПроверятьСтруктуру = Истина;
Запись.ОткрытьФайл(″c:\temp\combinedWrite.json″, , , ПараметрыJSON);
// Выполнить запись значений с помощью объекта записи (Запись).
// Записать начало корневого объекта.
Запись.ЗаписатьНачалоОбъекта();
Запись.ЗаписатьИмяСвойства(″ДатаФормирования″);
Запись.ЗаписатьЗначение(ЗаписатьДатуJSON(ТекущаяДата(), ФорматДатыJSON.ISO

, ВариантЗаписиДатыJSON.УниверсальнаяДата));
Запись.ЗаписатьИмяСвойства(″Оплаты″);
// Создать массив структур с данными документа ОплатаПоставщикам.
Оплаты = Новый Массив;
Пока СписокОплат.Следующий() Цикл

ОплатаПоставщику = Новый Структура(″Ссылка, Номер, Дата, Поставщик, Сумма″);

ОплатаПоставщику.Ссылка = Строка(СписокОплат.Ссылка);

ОплатаПоставщику.Номер = СписокОплат.Номер;

ОплатаПоставщику.Дата = СписокОплат.Дата;

ОплатаПоставщику.Поставщик = Строка(СписокОплат.Поставщик);

ОплатаПоставщику.Сумма = СписокОплат.Сумма;


Оплаты.Добавить(ОплатаПоставщику);
КонецЦикла;
// Выполнить запись данных (Оплаты) с помощью объекта записи (Запись).
ЗаписатьJSON(Запись, Оплаты, НастройкиСериализации);
// Записать конец корневого объекта.
Запись.ЗаписатьКонецОбъекта();
// Завершить работу с файлом.
Запись.Закрыть();
КонецПроцедуры

В процедуре сначала создается объект Запись, который открывает файл для
низкоуровневой записи. Затем с помощью потоковой техники в файл записывается дата формирования списка оплат как универсальная дата в формате
ISO.

46

Технологии интеграции «1С:Предприятия 8.3»

После этого создается массив Оплаты. Выборка документов ОплатаПоставщикам обходится в цикле, и на каждом шаге цикла структура
ОплатаПоставщику, заполненная данными этой выборки, добавляется
в массив Оплаты.
Затем с помощью объектной техники методом ЗаписатьJSON() массив
Оплаты записывается в JSON-документ.
В результате сформированный JSON-документ будет иметь следующий вид
(листинг 1.55).
Листинг 1.55. Содержимое JSON-документа
{
″ДатаФормирования″: ″2019-03-22T15:06:10Z″,
″Оплаты″: {
″0″: {
″Ссылка″: ″Оплата поставщикам 000000003 от 05.03.2019 12:00:00″,
″Номер″: ″000000003″,
″Дата″: ″2019-03-05T09:00:00Z″,
″Поставщик″: ″ООО \″Топаз\″″,
″Сумма″: 500500
},
″1″: {
″Ссылка″: ″Оплата поставщикам 000000001 от 13.03.2019 12:00:00″,
″Номер″: ″000000001″,
″Дата″: ″2019-03-13T09:00:00Z″,
″Поставщик″: ″ООО \″Стиль\″″,
″Сумма″: 300300
},
″2″: {
″Ссылка″: ″Оплата поставщикам 000000002 от 20.03.2019 12:00:00″,
″Номер″: ″000000002″,
″Дата″: ″2019-03-20T09:00:00Z″,
″Поставщик″: ″ОАО \″Фонтан\″″,
″Сумма″: 700700
}
}
}

HTTP-сервисы (REST)
Платформа «1С:Предприятие» позволяет прикладному разработчику реализовать собственные HTTP-сервисы. С помощью HTTP-сервисов можно
создать в прикладном решении дополнительный прикладной интерфейс,
доступ к которому из внешних систем осуществляется посредством HTTPзапросов.

Глава 1. Интернет-технологии

47

Общая информация
Одним из распространенных способов взаимодействия приложений в Интернете является использование веб-сервисов. Одно приложение (поставщик
веб-сервиса) публикует свой веб-сервис по уникальному веб-адресу в Интернете, а другие приложения (потребители веб-сервиса) могут обратиться
к этому веб-сервису, для того чтобы получить какие-то данные от поставщика, передать ему какие-то данные или заставить поставщика выполнить
какие-то действия.
Существуют разные протоколы и разные архитектуры, с помощью которых
реализуются веб-сервисы. В платформе поддерживается работа с двумя
видами веб-сервисов: веб-сервисы, реализованные в архитектуре REST
(в «1С:Предприятии» они называются HTTP-сервисы), и веб-сервисы,
работающие по протоколу SOAP (в «1С:Предприятии» они называются
Web-сервисы).
В этом разделе мы будем рассматривать HTTP-сервисы (REST) как наиболее
популярные и часто используемые. Про Web-сервисы (SOAP) вы можете
прочитать далее, в разделе «Web-сервисы».
Не вдаваясь в технические подробности, можно сказать, что HTTP-сервисы
пользуются популярностью благодаря следующим качествам:
■■
■■
■■
■■

простота программирования клиента таких сервисов;
потенциально меньший объем передаваемых данных;
потенциально меньшая вычислительная нагрузка;
HTTP-сервисы ориентированы на «ресурсы», в то время как SOAPсервисы ориентированы на «действия».
Первые три фактора особенно важны для приложений, работающих
на мобильных устройствах.
Обращение к HTTP-сервису выполняется по некоторому URL. В качестве
примера можно привести HTTP-сервис, который при обращении по одному
URL возвращает список каких-либо документов (например, расходных
накладных), а при обращении по другому URL возвращает конкретную
накладную. Действие, которое следует выполнить серверу, определяется тем,
какой HTTP-метод адресован серверу. Например, если на сервер отправляется
GET-запрос, то выполняется получение каких-либо данных, DELETE-запрос
приводит к удалению данных и т. д.

48

Технологии интеграции «1С:Предприятия 8.3»

При реализации HTTP-сервиса прикладной разработчик должен определить
следующие элементы сервиса:
■■ Базовую часть URL, по которому будет выполняться обращение к сервису.
■■ Состав предоставляемой функциональности и структуру ресурсов,
на которых эта функциональность будет отображаться.
■■ Действия (HTTP-методы), которые можно будет выполнить при обращении к тому или иному ресурсу.
■■ Для каждого выбранного действия разработчик должен создать
специальный метод, написанный на встроенном языке платформы
«1С:Предприятие», который и реализует необходимую функциональность.
Метод располагается в специальном модуле, связанном с HTTP-сервисом.
После реализации HTTP-сервиса необходимо опубликовать его на вебсервере с помощью стандартного механизма публикации.
Для обращения к HTTP-сервису нужно сформировать URL, который в общем
виде выглядит следующим образом: http://host/base/hs//. Рассмотрим более подробно составные
части адреса:
■■ http://host/base – это обычный URL, по которому выполняется
доступ, например, к информационной базе с помощью веб-клиента. Необходимо помнить, что при использовании информационной базы, в которой
настроено разделение данных, значения разделителей можно указывать
только в URL информационной базы. Указание разделителей с помощью
параметра Z не поддерживается;
■■ hs – признак того, что выполняется обращение к HTTP-сервису
(в отличие от ws, который определяет доступ к Web-сервису);
■■ – имя ресурса, которое определяет группу ресурсов,
связанных общим смыслом. Задается в свойстве КорневойURL объекта
HTTP-сервис;
■■ – определяет ресурс, к которому будет выполняться обращение. Относительный URL, указанный в запросе, будет
использован для определения конкретного ресурса, к которому выполняется обращение. Правило сопоставления задается в свойстве Шаблон
объекта Шаблон URL, подчиненного объекту HTTP-сервис.
ПОДРОБНЕЕ
Более подробно HTTP-сервисы описаны в документации «1С:Предприятия» в разделе «Глава 17. Механизмы интернет-сервисов – HTTPсервисы – HTTP-сервисы».

Глава 1. Интернет-технологии

49

Разработка HTTP-сервиса
Для создания HTTP-сервисов, которые будет поставлять прикладное
решение, есть специальный объект конфигурации HTTP-сервис. Такие
объекты добавляются в ветку Общие > HTTP-сервисы.
При создании HTTP-сервиса следует установить свойство Корневой URL.
Оно определяет группу ресурсов, объединенных общим смыслом. Например,
если необходимо создать несколько ресурсов, которые работают с заказами,
то корневой URL в этом случае может выглядеть как order. Тогда начало
URL при обращении к такому сервису будет выглядеть следующим образом:
http://host/base/hs/order.

Затем в каждом сервисе необходимо создать определенный шаблон URL
(группу ресурсов). Таких групп ресурсов может быть несколько. При
создании шаблона URL в свойстве Шаблон описываются возможныеадреса
ресурсов, которые можно будет использовать для обращения к HTTP-сервису.
При создании шаблона можно использовать следующие символы:
■■ любые символы, допустимые в идентификаторах языка «1С:Предприятия»;
■■ символ «/»;
■■ символы «{}» с непустым текстом между ними;
■■ символ *.
Например, шаблон может выглядеть следующим образом:
■■ /query
■■ /documents/{id}/props/{PropertyName}/*
В последнем примере props – это непараметризованный сегмент, который
должен быть перенесен в URL дословно. А заключенный в фигурные скобки
{PropertyName} – параметризованный сегмент, который фактически
описывает переменную с указанным именем, к которой можно получить
доступ из встроенного языка.
И затем для созданного шаблона необходимо определить HTTP-методы,
которые могут быть использованы при работе с ресурсом, совпадающим
с шаблоном. Для этого необходимо создать нужное количество объектов
Метод, подчиненных объекту Шаблон URL. Для каждого метода необходимо
указать свойство HTTP-метод и создать обработчик. В качестве HTTPметода можно выбрать один из следующих методов: GET, POST, PUT, DELETE,
PATCH, MERGE, CONNECT, OPTIONS, TRACE, PROPFIND, PROPPATCH, MKCOL,
COPY, MOVE, LOCK, UNLOCK. Или можно выбрать значение Любой – в этом
случае можно будет использовать любой метод из приведенного списка.

50

Технологии интеграции «1С:Предприятия 8.3»

При обращении к HTTP-сервису платформа сначала попытается сопоставить
URL, по которому произошло обращение, с одним из имеющихся шаблонов
и методов. Если сопоставить не удалось, то платформа выдаст код ответа:
404 – «Not Found». Если подходящий метод будет найден, то платформа
начнет выполнение его обработчика.
Обработчиком того или иного HTTP-метода является функция, которая получает на вход один параметр типа HTTPСервисЗапрос и должна вернуть
объект типа HTTPСервисОтвет.
Входной параметр полностью описывает HTTP-запрос, поступивший
в систему. С помощью объекта HTTPСервисЗапрос можно определить:

■■ какой HTTP-метод используется в запросе (свойство HTTPМетод);
■■ на какой URL поступил запрос (свойство БазовыйURL) – часть полного
URL запроса, не включающая относительный URL и параметры запроса;
■■ относительный URL (свойство ОтносительныйURL) – часть полного URL
запроса, которая использовалась для определения группы ресурсов
и правил обработки входящего HTTP-запроса;
■■ какие заголовки (свойство Заголовки) содержит HTTP-запрос;
■■ какие параметризованные сегменты (свойство ПараметрыURL) выделены
из входящего HTTP-запроса и их значения;
■■ какие параметры (свойство ПараметрыЗапроса) перечислены в URL HTTPзапроса после символа «?» и их значения.
Также существует возможность получать тело HTTP-запроса в виде
двоичных данных или строки (в зависимости от передаваемой информации).
После того как прикладное решение выполнило все действия, которые определены для данного ресурса и HTTP-метода, необходимо сформировать ответ
сервиса. Для этого необходимо создать объект типа HTTPСервисОтвет.
В зависимости от результата обработки следует указать свойство КодСостояния, которое описывает стандартный код возврата HTTP. Затем, при необходимости, следует сформировать тело ответа HTTP-сервиса. Тело может
быть установлено как двоичные данные, как строка (в формате JSON или
XML) или с помощью указания имени файла, откуда будет загружено тело
ответа.

Примеры реализации HTTP-сервисов
Рассмотрим все вышесказанное на конкретных примерах. Создадим два
HTTP-сервиса, с помощью которых мы будем получать информацию
о сотрудниках и информацию о ценах товаров из нашей демонстрационной
базы. Расположим примеры по мере усложнения и прокомментируем.

Глава 1. Интернет-технологии

51

ПОДРОБНЕЕ
Подробнее познакомиться с реализацией и использованием HTTP-сервисов
можно в демонстрационной конфигурации «Примеры работы», которая
прилагается к книге.

Информация о сотрудниках
Итак, в ветке Общие > HTTP-сервисы добавим HTTP-сервис с именем Сотрудники, который будет получать информацию из справочника Сотрудники.
Свойство Корневой URL для сервиса установим как employees (рис. 1.11).

Рис. 1.11. Основные свойства HTTP-сервиса «Сотрудники»

Поскольку в дальнейшем наш HTTP-сервис будет опубликован на локальном
сервере в каталоге «REST», то базовая часть URL для всех шаблонов
URL (групп ресурсов) при обращении к сервису будет выглядеть как
http://localhost/REST/hs/employees.

Предположим, нам нужно, чтобы при обращении по одному URL наш сервис
возвращал бы список всех сотрудников из справочника Сотрудники (возможно,
с отбором по признаку работы), а при обращении по другому URL мы получали бы все данные о конкретном сотруднике. И, кроме того, мы хотели бы
иметь возможность удаления конкретных сотрудников из справочника.

52

Технологии интеграции «1С:Предприятия 8.3»

Для этого нам потребуется создать у нашего HTTP-сервиса два подчиненных
объекта Шаблон URL с соответствующими шаблонами, с помощью которых
можно было бы сопоставить URL, по которому произошло обращение
к сервису, с одним из имеющихся HTTP-методов.
Итак, на закладке Шаблоны URL добавим у нашего HTTP-сервиса шаблон
URL с именем Список (свойство которого Шаблон оставим по умолчанию
в значении /*) и шаблон URL Сотрудник с шаблоном /code/{Код}
(рис. 1.12).

Рис. 1.12. Шаблоны URL HTTP-сервиса «Сотрудники»

Ниже мы разберем каждый шаблон URL более подробно.
Затем для каждого шаблона URL добавим метод, который будет вызван при
сопоставлении этого шаблона с URL запроса к HTTP-сервису. У каждого
метода выберем нужный HTTP-метод (GET, POST, PUT и т. п.) и создадим его
обработчик (рис. 1.13).

Глава 1. Интернет-технологии

53

Рис. 1.13. Шаблоны URL HTTP-сервиса «Сотрудники»

Получить список сотрудников
Итак, чтобы получить список сотрудников с возможным отбором из справочника Сотрудники, добавим у нашего HTTP-сервиса объект Шаблон URL
Список с шаблоном /*. Затем у этого объекта добавим подчиненный объект
Метод GET, в качестве HTTP-метода оставим предложенное по умолчанию
значение GET. Далее, нажав на кнопку открытия в поле Обработчик, создадим
обработчик этого метода (см. рис. 1.13). Имя обработчика автоматически
формируется путем соединения имени шаблона URL и имени самого метода.
Это имя можно изменить в дальнейшем по своему желанию, хотя делать это
не рекомендуется.
В модуле нашего HTTP-сервиса будет создан шаблон функции-обработчика
СписокGET(), которая получает на вход параметр типа HTTPСервисЗапрос
и должна вернуть объект типа HTTPСервисОтвет. Заполним эту функцию
следующим образом (листинг 1.56).

54

Технологии интеграции «1С:Предприятия 8.3»

Листинг 1.56. Функция «СписокGET»
Функция СписокGET(Запрос)
// Сформировать ответ, возвращаемый HTTP-сервисом.
Ответ = Новый HTTPСервисОтвет(200);
// Получить из запроса параметр URL *.
Признак = Запрос.ПараметрыURL[″*″];
// Сформировать выборку сотрудников.
Если Признак = ″″ Тогда

// Без отбора.

Выборка = Справочники.Сотрудники.Выбрать();
Иначе

// С отбором по признаку работы.

Работает = ?(ВРег(Прав(Признак,2)) = ″ДА″, Истина, Ложь);

Выборка = Справочники.Сотрудники.Выбрать(,, Новый Структура(″Работает″, Работает));
КонецЕсли;
// В цикле обхода выборки записать в JSON список сотрудников.
// Создать объект записи и записать строковое значение в строку JSON.
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
// Записать начало корневого объекта.
Запись.ЗаписатьНачалоОбъекта();
Пока Выборка.Следующий() Цикл

Запись.ЗаписатьИмяСвойства(Выборка.Наименование);

Запись.ЗаписатьНачалоОбъекта();

Запись.ЗаписатьИмяСвойства(″Код″);

Запись.ЗаписатьЗначение(Выборка.Код);

Запись.ЗаписатьИмяСвойства(″Должность″);

Запись.ЗаписатьЗначение(Строка(Выборка.Должность));

Запись.ЗаписатьКонецОбъекта();
КонецЦикла;
// Записать конец корневого объекта.
Запись.ЗаписатьКонецОбъекта();
// Записать результат записи в строку JSON.
Результат = Запись.Закрыть();
// Установить тело ответа из строки Результат.
Ответ.УстановитьТелоИзСтроки(Результат);
Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);
Возврат Ответ;
КонецФункции

Прокомментируем код функции.

Глава 1. Интернет-технологии

55

В переменной Ответ мы сразу же создаем объект типа HTTPСервисОтвет,
который содержит ответ, формируемый HTTP-сервером на поступивший
запрос. Ответ имеет стандартный код возврата HTTP (КодСостояния=200).
Код состояния 200 сообщает об успешном выполнении запроса. Поскольку
в функции мы описываем простейшее действие – получение выборки
данных из справочника, то для простоты мы не будем здесь анализировать
ошибочные состояния, которые могут возникнуть при выполнении этого
обработчика.
ПОДРОБНЕЕ
Подробнее о кодах состояния, возвращаемых HTTP-сервером,
рассказывается в разделе «Коды состояния в ответах HTTP-сервера».

Родительский объект функции-обработчика – это шаблон URL Список,
который содержит строку шаблона /*. Этот шаблон означает, что в относительном URL на месте звездочки могут оказаться любые допустимые
символы, в том числе возможно и отсутствие этих символов.
Поэтому мы получаем и анализируем те символы, которые в реальном URL
присутствуют на месте звездочки. Для этого мы получаем параметры URL
запроса, который передается в функцию в параметре Запрос с помощью
конструкции Запрос.ПараметрыURL[«*»].
Если относительный URL пустой, то мы формируем выборку из справочника
Сотрудники без отбора. Если на месте звездочки находится строка «ДА», то
она интерпретируется как Истина, любые другие символы интерпретируется как Ложь. Соответственно этому признаку накладывается отбор на поле
справочника Работает.

В цикле обхода выборки мы заполняем строку JSON списком сотрудников
и затем записываем эту строку в переменную Результат. После этого
с помощью метода УстановитьТелоИзСтроки() устанавливаем тело
ответа HTTP-сервиса из строки Результат и возвращаем ответ сервиса
в переменной Ответ.

При этом заголовок «Content-type» объекта HTTPСервисОтвет мы устанавливаем как «application/json». Такой заголовок помогает клиенту понять, что
же за данные к нему пришли, кроме того, из него определяется кодировка
ответа, если она не задана явно.
Теперь посмотрим, как это работает.
Прежде всего нам нужно опубликовать наш HTTP-сервис на веб-сервере.
Для этого в диалоге публикации информационной базы на веб-сервере
на закладке HTTP сервисы установим флажок Публиковать HTTP сервисы

56

Технологии интеграции «1С:Предприятия 8.3»

по умолчанию и отметим имеющиеся HTTP-сервисы, к которым мы хотим

предоставить доступ из внешних систем (рис. 1.14).

Рис. 1.14. Публикация HTTP-сервиса на веб-сервере

И все, больше делать ничего не надо.
Поскольку в большинстве наших примеров мы будем просто получать
данные информационной базы методом GET, то URL запроса к HTTP-сервису
будем набирать непосредственно в адресной строке браузера и сразу же
в окне браузера будем видеть содержимое ответа сервиса.
Заметим, что в этом разделе таким способом (получение данных через
браузер) мы будем пользоваться только для упрощения примеров. На самом
же деле к подобным HTTP-сервисам обращаются с помощью HTTP-запросов.
Про то, как в «1С:Предприятии» написать собственный HTTP-запрос и с его
помощью обратиться к HTTP-сервису, будет рассказано в следующем разделе
«HTTP-запросы».
Итак, наберем в адресной строке браузера следующий URL (листинг 1.57).
Листинг 1.57. URL запроса
http://localhost/REST/hs/employees

Глава 1. Интернет-технологии

57

Как видите, для получения списка сотрудников из справочника мы указываем только базовую часть URL запроса к HTTP-сервису, относительная
часть URL отсутствует. Поэтому такой URL будет сопоставлен с шаблоном
URL Список (строка шаблона – /*) и будет вызван обработчик этого шаблона
СписокGET(). В результате в окне браузера мы увидим список всех сотрудников из справочника Сотрудники (листинг 1.58).
Листинг 1.58. Содержимое ответа сервиса
{
″Алексеев Сергей Иванович″: {
″Код″: ″000000001″,
″Должность″: ″Администратор″
},
″Артемов Игорь Владимирович″: {
″Код″: ″REST-0003″,
″Должность″: ″Новая должность - REST″
},
″Смирнова Светлана Ивановна″: {
″Код″: ″000000002″,
″Должность″: ″Кассир″
}
}

Теперь наберем в адресной строке браузера следующий URL (листинг 1.59).
Листинг 1.59. URL запроса
http://localhost/REST/hs/employees/да

Такой URL будет сопоставлен с шаблоном URL Список (строка шаблона – /*)
и будет вызван обработчик этого шаблона СписокGET(). В результате в окне
браузера мы увидим список только работающих сотрудников (листинг 1.60).
Листинг 1.60. Содержимое ответа сервиса
{
″Алексеев Сергей Иванович″: {
″Код″: ″000000001″,
″Должность″: ″Администратор″
},
″Артемов Игорь Владимирович″: {
″Код″: ″REST-0003″,
″Должность″: ″Новая должность - REST″
}
}

Теперь наберем в адресной строке браузера следующий URL (листинг 1.61).

58

Технологии интеграции «1С:Предприятия 8.3»

Листинг 1.61. URL запроса
http://localhost/REST/hs/employees/нет

В результате в окне браузера мы увидим список только неработающих
сотрудников (листинг 1.62).
Листинг 1.62. Содержимое ответа сервиса
{
″Смирнова Светлана Ивановна″: {
″Код″: ″000000002″,
″Должность″: ″Кассир″
}
}

Получить данные о конкретном сотруднике
В этом примере, чтобы получить данные о конкретном сотруднике, нам понадобится воспользоваться другим шаблоном URL нашего HTTP-сервиса.
В относительном URL запроса при обращении к сервису должно быть
указано некое уникальное свойство (например, Код), по которому сотрудника
можно было бы идентифицировать в справочнике. В обработчике, связанном
с этим шаблоном, данные о конкретном сотруднике должны быть записаны
в строку JSON и подставлены в тело ответа, возвращаемого сервисом.
Итак, добавим у нашего HTTP-сервиса объект Шаблон URL Сотрудник
с шаблоном /code/{Код}. Затем у этого объекта добавим подчиненный
метод GET, в качестве HTTP-метода оставим предложенное по умолчанию
значение GET. Далее создадим обработчик этого метода.
В модуле нашего HTTP-сервиса будет создан шаблон функции-обработчика
СотрудникGET(). Заполним эту функцию следующим образом (листинг
1.63).
Листинг 1.63. Функция «СотрудникGET»
Функция СотрудникGET(Запрос)
// Сформировать ответ, возвращаемый HTTP-сервисом.
Ответ = Новый HTTPСервисОтвет(200);
// Получить из запроса параметр URL Код.
Код = Запрос.ПараметрыURL.Получить(″Код″);
Если Код = Неопределено Тогда

Ответ = Новый HTTPСервисОтвет(400);

Ответ.УстановитьТелоИзСтроки(″Не задан параметр Код″);

Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);
Возврат Ответ;

Глава 1. Интернет-технологии

59

КонецЕсли;
// Найти сотрудника в справочнике по полученному параметру Код.
СотрудникСсылка = Справочники.Сотрудники.НайтиПоКоду(Код);
Если СотрудникСсылка = Справочники.Сотрудники.ПустаяСсылка() Тогда

Ответ = Новый HTTPСервисОтвет(404);

Ответ.УстановитьТелоИзСтроки(″Сотрудник не найден″);

Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);
Возврат Ответ;
КонецЕсли;
Сотрудник = СотрудникСсылка.ПолучитьОбъект();
// Сериализовать данные объекта Сотрудник с помощью объекта записи (Запись).
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, Символы.Таб);
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку(ПараметрыЗаписиJSON);
СериализаторXDTO.ЗаписатьJSON(Запись, Сотрудник);
// Записать результат записи в строку JSON.
Результат = Запись.Закрыть();
// Установить тело ответа из строки Результат.
Ответ.УстановитьТелоИзСтроки(Результат);
Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);
Возврат Ответ;
КонецФункции

Прокомментируем код функции.
Родительский объект функции-обработчика – шаблон URL Сотрудник,
описанный с помощью шаблона /code/{Код}. Этот шаблон содержит
непараметризованный сегмент code и параметризованный сегмент {Код}.
Это означает, что любые символы, которые могут присутствовать в URL
запроса к сервису после символов «/code/», будут трактоваться как параметр
URL Код, который можно получить из переданного в функцию параметра
Запрос.
Поэтому сначала в обработчике мы получаем значение параметра Код
(Запрос.ПараметрыURL.Получить(«Код»)) и анализируем его. Если
этот параметр URL не определен, то мы прекращаем работу функции
и возвращаем ответ сервиса с кодом состояния 400, сообщающим об ошибке
запроса.
Затем находим сотрудника в справочнике Сотрудники по значению параметра
Код. Если сотрудник не найден, то мы прекращаем работу функции и возвращаем ответ сервиса с кодом состояния 404, сообщающим об этом.

60

Технологии интеграции «1С:Предприятия 8.3»

От ссылки на найденного сотрудника мы получаем объект и сериализуем
данные сотрудника в JSON с помощью метода ЗаписатьJSON() объекта
глобального контекста СериализаторXDTO. Результат записи сохраняем
в строку JSON в переменную Результат. После этого с помощью метода
УстановитьТелоИзСтроки() устанавливаем тело ответа HTTP-сервиса
из строки Результат и возвращаем ответ сервиса в переменной Ответ.
Теперь посмотрим, как это работает. Наберем в адресной строке браузера
следующий URL (листинг 1.64).
Листинг 1.64. URL запроса
http://localhost/REST/hs/employees/code/000000001

В URL запроса к HTTP-сервису после символов «/code/» присутствует строковый код сотрудника (000000001). Поэтому такой URL будет сопоставлен
с шаблоном URL Сотрудник (строка шаблона – /code/{Код}) и будет вызван
обработчик этого шаблона СотрудникGET(). В результате в окне браузера
мы увидим содержимое ответа сервиса (листинг 1.65).
Листинг 1.65. Содержимое ответа сервиса
{

″#value″: {

″Ref″: ″9575f8ed-369f-11e9-8bb2-642737df2048″,

″DeletionMark″: false,

″Code″: ″000000001″,

″Description″: ″Алексеев Сергей Иванович″,

″ДатаРождения″: ″1980-12-10T00:00:00″,

″КоличествоДетей″: 1,

″Работает″: true,

″Стаж″: ″10.05.01″,

″Должность″: ″88d54406-36a1-11e9-8bb2-642737df2048″
}
}

Удалить данные о конкретном сотруднике
В этом примере, чтобы иметь возможность удалить данные о конкретном
сотруднике, мы добавим еще один метод для ранее созданного шаблона URL
Сотрудник нашего HTTP-сервиса.
Итак, добавим этому шаблону еще один подчиненный метод DELETE,
в качестве HTTP-метода выберем значение DELETE. Далее создадим обработчик этого метода.

Глава 1. Интернет-технологии

61

В модуле нашего HTTP-сервиса будет создан шаблон функции-обработчика СотрудникDELETE(). Заполним эту функцию следующим образом
(листинг 1.66).
Листинг 1.66. Функция «СотрудникDELETE»
Функция СотрудникDELETE(Запрос)
// Сформировать ответ (без тела), возвращаемый HTTP-сервисом.
Ответ = Новый HTTPСервисОтвет(204);
// Получить из запроса параметр URL Код.
Код = Запрос.ПараметрыURL.Получить(″Код″);
Если Код = Неопределено Тогда

Ответ = Новый HTTPСервисОтвет(400);

Ответ.УстановитьТелоИзСтроки(″Не задан параметр Код″);

Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);
Возврат Ответ;
КонецЕсли;
// Найти сотрудника в справочнике по полученному параметру Код.
СотрудникСсылка = Справочники.Сотрудники.НайтиПоКоду(Код);
Если СотрудникСсылка = Справочники.Сотрудники.ПустаяСсылка() Тогда

Ответ = Новый HTTPСервисОтвет(404);

Ответ.УстановитьТелоИзСтроки(″Сотрудник не найден″);

Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);
Возврат Ответ;
КонецЕсли;
// Установить найденному сотруднику пометку удаления.
Сотрудник = СотрудникСсылка.ПолучитьОбъект();
Сотрудник.УстановитьПометкуУдаления(Истина);
Сотрудник.Записать();
Возврат Ответ;
КонецФункции

Поиск сотрудника по значению параметра URL запроса аналогичен поиску
в обработчике СотрудникGET() из предыдущего примера (см. листинг
1.63). Затем от ссылки на найденного сотрудника мы получаем объект и устанавливаем ему пометку удаления.
И в заключение возвращаем ответ HTTP-сервиса с кодом состояния 204,
который также свидетельствует об успешном выполнении операции
на сервере, но сообщает клиенту, что сервер не посылал в ответ никаких
данных в теле запроса.
Испытать наш запрос в браузере не получится, так как из браузера отправляются только запросы на чтение (методом GET) данных из информационной
базы. Чтобы посмотреть результат работы HTTP-сервиса по удалению

62

Технологии интеграции «1С:Предприятия 8.3»

данных о конкретном сотруднике в прикладном решении, надо обратиться
к нему с помощью собственного HTTP-запроса и выполнить на сервере
метод DELETE. Этот пример будет рассмотрен в следующем разделе «HTTPзапросы – Удаление данных».
Информация о ценах на товары
В этом примере мы рассмотрим, как с помощью HTTP-сервиса получать
информацию о ценах товаров, которые в нашей демонстрационной конфигурации хранятся в регистре сведений ЦеныТоваров.
Для этого в ветке Общие > HTTP-сервисы добавим HTTP-сервис с именем
ЦеныТоваров, свойство которого Корневой URL установим как prices.
Поскольку в дальнейшем наш HTTP-сервис будет опубликован на локальном
сервере в каталоге «REST», то базовая часть URL для всех шаблонов
URL (групп ресурсов) при обращении к сервису будет выглядеть как
http://localhost/REST/hs/prices.
Предположим, нам нужно, чтобы при обращении по одному URL наш сервис
возвращал бы список цен всех товаров, актуальных на определенную дату,
а при обращении по другому URL мы получали бы последнюю установленную цену на конкретный товар. И, кроме того, мы хотели бы иметь
возможность индексации последних цен всех товаров на заданную величину.

Для этого на закладке Шаблоны URL добавим у нашего HTTP-сервиса шаблон
URL ПолучитьЦены с шаблоном /date/{Дата}, шаблон URL ПоказатьЦенуТовара с шаблоном /product/{Код} и шаблон URL ПроиндексироватьЦены
с шаблоном /percent/{Процент}.

Затем для каждого шаблона URL добавим метод, который будет вызван при
сопоставлении этого шаблона с URL запроса к HTTP-сервису. Для методов
ПолучитьЦены и ПоказатьЦенуТовара выберем HTTP-метод GET, а для метода
ПроиндексироватьЦены выберем HTTP-метод POST (рис. 1.15).

Глава 1. Интернет-технологии

63

Рис. 1.15. Шаблоны URL и методы HTTP-сервиса «Цены товаров»

Получить цены товаров на определенную дату
В этом примере, чтобы получить актуальные цены товаров на заданную дату,
нам понадобится получить срез последних записей на эту дату из регистра
сведений ЦеныТоваров и подставить список товаров их цен в тело ответа,
возвращаемого сервисом.
Итак, добавим у нашего HTTP-сервиса объект Шаблон URL ПолучитьЦены
с шаблоном /date/{Дата}. Затем у этого объекта добавим подчиненный
метод GET, в качестве HTTP-метода оставим предложенное по умолчанию
значение GET. Далее создадим обработчик этого метода.
В модуле нашего HTTP-сервиса будет создан шаблон функции-обработчика
ПолучитьЦеныGET(). Заполним эту функцию следующим образом (листинг
1.67).
Листинг 1.67. Функция «ПолучитьЦеныGET»
Функция ПолучитьЦеныGET(Запрос)
Попытка


// Получить из запроса параметр URL Дата.

ДатаЗапроса = Запрос.ПараметрыURL.Получить(″Дата″);

64

Технологии интеграции «1С:Предприятия 8.3»


Если ДатаЗапроса = Неопределено Тогда

Ответ = Новый HTTPСервисОтвет(400);

Ответ.УстановитьТелоИзСтроки(″Не задан параметр Дата″);

Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);

Возврат Ответ;

КонецЕсли;


ЦеныНаДату = Дата(ДатаЗапроса);

// Сформировать запрос к регистру сведений.

Запрос = Новый Запрос;

Запрос.Текст = ″ВЫБРАТЬ

|
ЦеныТоваровСрезПоследних.Период КАК Период,

|
ЦеныТоваровСрезПоследних.Товар КАК Товар,

|
ЦеныТоваровСрезПоследних.Цена КАК Цена

|ИЗ

|
РегистрСведений.ЦеныТоваров.СрезПоследних(&ЦеныНаДату, )

|
КАК ЦеныТоваровСрезПоследних″;


Запрос.УстановитьПараметр(″ЦеныНаДату″, ЦеныНаДату);

Выборка = Запрос.Выполнить().Выбрать();


// В цикле обхода выборки записать в JSON последние цены товаров

// Создать объект записи и записать строковое значение в строку JSON.

Запись = Новый ЗаписьJSON;

Запись.УстановитьСтроку();


// Записать начало корневого объекта.

Запись.ЗаписатьНачалоОбъекта();

Пока Выборка.Следующий() Цикл

Запись.ЗаписатьИмяСвойства(Строка(Выборка.Товар));

Запись.ЗаписатьНачалоОбъекта();

Запись.ЗаписатьИмяСвойства(″Период″);

Запись.ЗаписатьЗначение(Строка(Выборка.Период));

Запись.ЗаписатьИмяСвойства(″Цена″);

Запись.ЗаписатьЗначение(Выборка.Цена);

Запись.ЗаписатьКонецОбъекта();

КонецЦикла;

// Записать конец корневого объекта.

Запись.ЗаписатьКонецОбъекта();


// Записать результат записи в строку JSON.

Результат = Запись.Закрыть();


// Сформировать ответ, возвращаемый HTTP-сервисом.

Ответ = Новый HTTPСервисОтвет(200);

// Установить тело ответа из строки Результат.

Ответ.УстановитьТелоИзСтроки(Результат);

Исключение


// Вывести стуктурированную информацию об исключении.

Ответ = Новый HTTPСервисОтвет(500);

Информация = ИнформацияОбОшибке();

Сообщение = Информация.Описание;

Глава 1. Интернет-технологии

65


Если Информация.Причина Неопределено Тогда

Сообщение = Сообщение + ″:″ + Информация.Причина.Описание;

КонецЕсли;

Ответ.УстановитьТелоИзСтроки(Сообщение);

КонецПопытки;
Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);
Возврат Ответ;
КонецФункции

Прокомментируем код функции.
Родительский объект функции-обработчика – шаблон URL ПолучитьЦены,
описанный с помощью шаблона /date/{Дата}. Этот шаблон содержит
непараметризованный сегмент date и параметризованный сегмент {Дата}.
Это означает, что любые символы, которые могут присутствовать в URL
запроса к сервису после символов «/date/», будут трактоваться как параметр
URL Дата, который можно получить из переданного в функцию параметра
Запрос.
Поэтому сначала в обработчике мы делаем попытку получить значение
параметра Дата (Запрос.ПараметрыURL.Получить("Дата")) и анализируем его. Если этот параметр URL не определен, то мы прекращаем работу
функции и возвращаем ответ сервиса с кодом состояния 400, сообщающим
об ошибке запроса.
Затем мы преобразуем строковое значение параметра Дата в дату ЦеныНаДату, которую подставляем в качестве параметра в запрос, обращающийся
к срезу последних регистра ЦеныТоваров.
В цикле обхода выборки мы заполняем строку JSON списком последних
цен товаров на дату, переданную в параметре запроса, и затем записываем эту строку в переменную Результат. После этого создаем объект
HTTPСервисОтвет с кодом состояния 200, с помощью метода УстановитьТелоИзСтроки() устанавливаем тело ответа HTTP-сервиса из строки
Результат и возвращаем ответ сервиса в переменной Ответ.

При этом заголовок «Content-type» объекта HTTPСервисОтвет мы устанавливаем как «application/json». Такой заголовок помогает клиенту понять, что
же за данные к нему пришли, кроме того, из него определяется кодировка
ответа, если она не задана явно.
Если же при попытке получить значение параметра URL из запроса возникает ошибка, то генерируется исключение. С помощью метода глобального
контекста ИнформацияОбОшибке() мы получаем структурированную

66

Технологии интеграции «1С:Предприятия 8.3»

информацию об исключении и возвращаем ответ сервиса с кодом состояния
500, сообщающим о причине ошибки.
Теперь посмотрим, как это работает.
Прежде всего опубликуем наш HTTP-сервис на веб-сервере. О том, как
это делается, уже рассказывалось в примере про HTTP-сервис Сотрудники
(см. рис. 1.14).
После этого наберем в адресной строке браузера следующий URL (листинг
1.68).
Листинг 1.68. URL запроса
http://localhost/REST/hs/prices/date/20190510

В URL запроса к HTTP-сервису после символов «/date/» присутствует
строковое представление даты – 10/05/2019. Поэтому такой URL будет сопоставлен с шаблоном URL ПолучитьЦены (строка шаблона – /date/{Дата})
и будет вызван обработчик этого шаблона ПолучитьЦеныGET(). В результате в окне браузера мы увидим содержимое ответа сервиса (листинг 1.69).
Листинг 1.69. Содержимое ответа сервиса
{
″Чайник″: {
″Период″: ″07.05.2019 0:00:00″,
″Цена″: 3000
},
″Тостер″: {
″Период″: ″07.05.2019 0:00:00″,
″Цена″: 2000
},
″Кофемашина″: {
″Период″: ″07.05.2019 0:00:00″,
″Цена″: 5000
}
}

Получить данные о ценах на конкретный товар
В этом примере, чтобы получить последнюю установленную цену
на конкретный товар, нам понадобится воспользоваться другим шаблоном
URL нашего HTTP-сервиса. В относительном URL запроса при обращении
к сервису должно быть указано некое уникальное свойство (например, Код),
по которому товар можно было бы идентифицировать в справочнике товаров.
В обработчике, связанном с этим шаблоном, нужно получить последнюю
цену товара из регистра сведений ЦеныТоваров и подставить ее в тело ответа,
возвращаемого сервисом.

Глава 1. Интернет-технологии

67

Итак, добавим у нашего HTTP-сервиса объект Шаблон URL ПоказатьЦенуТовара с шаблоном /product/{Код}. Затем у этого объекта добавим
подчиненный метод GET, в качестве HTTP-метода оставим предложенное
по умолчанию значение GET. Далее создадим обработчик этого метода.
В модуле нашего HTTP-сервиса будет создан шаблон функции-обработчика
ПоказатьЦенуТовараGET(). Заполним эту функцию следующим образом
(листинг 1.70).
Листинг 1.70. Функция «ПоказатьЦенуТовараGET»
Функция ПоказатьЦенуТовараGET(Запрос)
Попытка


// Получить из запроса параметр URL Код.

Код = Запрос.ПараметрыURL.Получить(″Код″);

Если Код = Неопределено Тогда

Ответ = Новый HTTPСервисОтвет(400);

Ответ.УстановитьТелоИзСтроки(″Не задан параметр Код″);

Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);

Возврат Ответ;

КонецЕсли;


// Найти товар в справочнике по полученному параметру Код.

Товар = Справочники.Товары.НайтиПоКоду(Код);

Если Товар = Справочники.Товары.ПустаяСсылка() Тогда

Ответ = Новый HTTPСервисОтвет(400);

Ответ.УстановитьТелоИзСтроки(″Товар не найден″);

Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);

Возврат Ответ;

КонецЕсли;


// Получить последнюю цену товара из регистра сведений.

ЦенаТовара = РегистрыСведений.ЦеныТоваров.ПолучитьПоследнее(

, Новый Структура(″Товар″, Товар));

// Создать объект записи и записать строковое значение в строку JSON.

Запись = Новый ЗаписьJSON;

Запись.УстановитьСтроку();


// Записать начало корневого объекта.

Запись.ЗаписатьНачалоОбъекта();

Запись.ЗаписатьИмяСвойства(″На: ″);

Запись.ЗаписатьЗначение(Строка(ТекущаяДата()));

Запись.ЗаписатьИмяСвойства(″Товар″);

Запись.ЗаписатьЗначение(Строка(Товар));

Запись.ЗаписатьИмяСвойства(″Цена″);

Запись.ЗаписатьЗначение(ЦенаТовара.Цена);

// Записать конец корневого объекта.

Запись.ЗаписатьКонецОбъекта();


// Записать результат записи в строку JSON.

68

Технологии интеграции «1С:Предприятия 8.3»



Результат = Запись.Закрыть();




// Сформировать ответ, возвращаемый HTTP-сервисом.
Ответ = Новый HTTPСервисОтвет(200);


// Установить тело ответа из строки Результат.

Ответ.УстановитьТелоИзСтроки(Результат);

Исключение


// Вывести стуктурированную информацию об исключении.

Ответ = Новый HTTPСервисОтвет(500);

Информация = ИнформацияОбОшибке();

Сообщение = Информация.Описание;

Если Информация.Причина Неопределено Тогда

Сообщение = Сообщение + ″:″ + Информация.Причина.Описание;

КонецЕсли;

Ответ.УстановитьТелоИзСтроки(Сообщение);

КонецПопытки;
Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);
Возврат Ответ;
КонецФункции

Прокомментируем код функции.
Родительский объект функции-обработчика – шаблон URL ПоказатьЦенуТовара, описанный с помощью шаблона /product/{Код}. Этот шаблон
содержит непараметризованный сегмент product и параметризованный
сегмент {Код}. Это означает, что любые символы, которые могут присутствовать в URL запроса к сервису после символов «/product/», будут трактоваться
как параметр URL Код, который можно получить из переданного в функцию
параметра Запрос.

Поэтому сначала в обработчике мы делаем попытку получить значение
параметра Код (Запрос.ПараметрыURL.Получить("Код")) и анализируем его. Если этот параметр URL не определен, то мы прекращаем работу
функции и возвращаем ответ сервиса с кодом состояния 400, сообщающим
об ошибке запроса.
Затем находим товар в справочнике Товары по значению параметра Код. Если
товар не найден, то мы прекращаем работу функции и возвращаем ответ
сервиса с кодом состояния 404, сообщающим об этом.
После этого с помощью метода ПолучитьПоследнее() с отбором по
найденному товару мы получаем последнюю цену этого товара из регистра
сведений ЦеныТоваров и записываем ее в JSON-строку Результат.

Глава 1. Интернет-технологии

69

И затем создаем объект HTTPСервисОтвет с кодом состояния 200,
с помощью метода УстановитьТелоИзСтроки() устанавливаем тело
ответа HTTP-сервиса из строки Результат и возвращаем ответ сервиса
в переменной Ответ.

Если же при попытке получить значение параметра URL из запроса возникает ошибка, то генерируется исключение. С помощью метода глобального
контекста ИнформацияОбОшибке() мы получаем структурированную
информацию об исключении и возвращаем ответ сервиса с кодом состояния
500, сообщающим о причине ошибки.
Теперь посмотрим, как это работает. Наберем в адресной строке браузера
следующий URL (листинг 1.71).
Листинг 1.71. URL запроса
http://localhost/REST/hs/prices/product/000000001

В URL запроса к HTTP-сервису после символов «/product/» присутствует
строковой код товара (000000001). Поэтому такой URL будет сопоставлен
с шаблоном URL ПоказатьЦенуТовара (строка шаблона – /product/{Код})
и будет вызван обработчик этого шаблона ПоказатьЦенуТовараGET().
В результате в окне браузера мы увидим содержимое ответа сервиса (листинг
1.72).
Листинг 1.72. Содержимое ответа сервиса
{
″На: ″: ″21.07.2019 13:06:01″,
″Товар″: ″Чайник″,
″Цена″: 3300
}

Проиндексировать цены на товары
В этом примере, чтобы проиндексировать последние цены товаров на
заданную величину, нам понадобится воспользоваться другим шаблоном
URL нашего HTTP-сервиса. В обработчике, связанном с этим шаблоном, нам
нужно получить срез последних записей из регистра сведений ЦеныТоваров
и затем увеличить последние цены товаров на значение, полученное из URL
запроса при обращении к сервису. После этого нужно записать в регистр
сведений новые цены товаров на текущую дату и затем показать эти новые
цены в теле ответа, возвращаемого сервисом.
Итак, добавим у нашего HTTP-сервиса объект Шаблон URL ПроиндексироватьЦены с шаблоном /percent/{Процент}. Затем у этого объекта добавим

70

Технологии интеграции «1С:Предприятия 8.3»

подчиненный метод POST, в качестве HTTP-метода оставим предложенное
по умолчанию значение POST. Далее создадим обработчик этого метода.
В модуле нашего HTTP-сервиса будет создан шаблон функции-обработчика
ПроиндексироватьЦеныPOST(). Заполним эту функцию следующим
образом (листинг 1.73).
Листинг 1.73. Функция «ПроиндексироватьЦеныPOST»
Функция ПроиндексироватьЦеныPOST(Запрос)
Попытка


// Получить из запроса параметр URL Процент.

Процент = Запрос.ПараметрыURL.Получить(″Процент″);

Если Процент = Неопределено Тогда

Ответ = Новый HTTPСервисОтвет(400);

Ответ.УстановитьТелоИзСтроки(″Не задан параметр Процент″);

Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);

Возврат Ответ;

КонецЕсли;


Коэффициент = 1 + Число(Процент)/100;

// Сформировать запрос к регистру сведений.

Запрос = Новый Запрос;

Запрос.Текст = ″ВЫБРАТЬ

| ЦеныТоваровСрезПоследних.Период КАК Период,

| ЦеныТоваровСрезПоследних.Товар КАК Товар,

| ЕСТЬNULL(ЦеныТоваровСрезПоследних.Цена, 0) КАК Цена

|ИЗ

| РегистрСведений.ЦеныТоваров.СрезПоследних(, ) КАК ЦеныТоваровСрезПоследних″;


Выборка = Запрос.Выполнить().Выбрать();

// В цикле обхода выборки записать в JSON и добавить

// в регистр сведений последние цены товаров.

// Создать объект записи и записать строковое значение в строку JSON.

Запись = Новый ЗаписьJSON;

Запись.УстановитьСтроку();


// Записать начало корневого объекта.

Запись.ЗаписатьНачалоОбъекта();

Пока Выборка.Следующий() Цикл

НоваяЦена = Выборка.Цена * Коэффициент;


Запись.ЗаписатьИмяСвойства(Строка(Выборка.Товар));

Запись.ЗаписатьНачалоОбъекта();

Запись.ЗаписатьИмяСвойства(″Период″);

Запись.ЗаписатьЗначение(Строка(ТекущаяДата()));

Запись.ЗаписатьИмяСвойства(″Цена″);

Запись.ЗаписатьЗначение(НоваяЦена);

Запись.ЗаписатьКонецОбъекта();


// Подготовить менеджер записи.

Глава 1. Интернет-технологии

71


ЗаписьРегистра = РегистрыСведений.ЦеныТоваров.СоздатьМенеджерЗаписи();


// Установить ключевые поля менеджера записи.

ЗаписьРегистра.Период = ТекущаяДата();

ЗаписьРегистра.Товар = Выборка.Товар;

ЗаписьРегистра.Цена = НоваяЦена;


// Записать новую запись в базу данных.

ЗаписьРегистра.Записать();

КонецЦикла;

// Записать конец корневого объекта.

Запись.ЗаписатьКонецОбъекта();


// Записать результат записи в строку JSON.

Результат = Запись.Закрыть();

// Сформировать ответ, возвращаемый HTTP-сервисом.

Ответ = Новый HTTPСервисОтвет(200);


// Установить тело ответа из строки Результат.

Ответ.УстановитьТелоИзСтроки(Результат);

Исключение


// Вывести стуктурированную информацию об исключении.

Ответ = Новый HTTPСервисОтвет(500);

Информация = ИнформацияОбОшибке();

Сообщение = Информация.Описание;

Если Информация.Причина Неопределено Тогда

Сообщение = Сообщение + ″:″ + Информация.Причина.Описание;

КонецЕсли;

Ответ.УстановитьТелоИзСтроки(Сообщение);

КонецПопытки;
Ответ.Заголовки.Вставить(″Content-type″, ″application/json″);
Возврат Ответ;
КонецФункции

Прокомментируем код функции.
Родительский объект функции-обработчика – шаблон URL ПроиндексироватьЦены, описанный с помощью шаблона /percent/{Процент}. Этот шаблон
содержит непараметризованный сегмент percent и параметризованный
сегмент {Процент}. Это означает, что любые символы, которые могут
присутствовать в URL запроса к сервису после символов «/percent/», будут
трактоваться как параметр URL Процент, который можно получить из переданного в функцию параметра Запрос.

72

Технологии интеграции «1С:Предприятия 8.3»

Поэтому сначала в обработчике мы делаем попытку получить значение
параметра Процент (Запрос.ПараметрыURL.Получить("Процент"))
и анализируем его. Если этот параметр URL не определен, то мы прекращаем
работу функции и возвращаем ответ сервиса с кодом состояния 400, сообщающим об ошибке запроса.
Затем мы преобразуем строковое значение параметра Процент в числовой
коэффициент, на который нам нужно увеличить последние цены товаров.
После этого выполняем запрос, обращающийся к срезу последних регистра
ЦеныТоваров. В цикле обхода выборки из результата запроса мы умножаем
последние цены товаров на полученный нами коэффициент, заполняем
строку JSON списком новых цен и записываем эту строку в переменную
Результат.
Затем с помощью менеджера записи записываем новые цены товаров
на текущую дату в регистр сведений.

После этого создаем объект HTTPСервисОтвет с кодом состояния 200,
с помощью метода УстановитьТелоИзСтроки() устанавливаем тело
ответа HTTP-сервиса из строки Результат и возвращаем ответ сервиса
в переменной Ответ.

Если же при попытке получить значение параметра URL из запроса возникает ошибка, то генерируется исключение. С помощью метода глобального
контекста ИнформацияОбОшибке() мы получаем структурированную
информацию об исключении и возвращаем ответ сервиса с кодом состояния
500, сообщающим о причине ошибки.
Чтобы посмотреть результат работы HTTP-сервиса по индексации цен
товаров в прикладном решении, надо обратиться к нему с помощью собственного HTTP-запроса и выполнить на сервере метод POST. Этот пример будет
рассмотрен в следующем разделе «HTTP-запросы – Добавление данных».

HTTP-сервисы в расширениях
В расширениях также можно создавать собственные HTTP-сервисы. Таким
образом, в процессе внедрения, не изменяя конфигурацию, находящуюся на
поддержке, можно настроить взаимодействие прикладного решения с внешними системами. Например, для синхронизации данных между прикладным
решением и веб-сайтом.
Для этого в расширении в ветку Общие > HTTP-сервисы нужно добавить
HTTP-сервис, разработать его по аналогии с описанными выше примерами
и опубликовать на веб-сервере, установив в диалоге публикации флажок
Публиковать HTTP сервисы расширений по умолчанию.

Глава 1. Интернет-технологии

73

В отличие от HTTP-сервисов, созданных в основной конфигурации, установка в диалоге публикации флажка Публиковать HTTP сервисы расширений
по умолчанию не позволяет выбирать для публикации конкретные сервисы
расширений. Поэтому если требуется опубликовать лишь некоторые из них,
то можно вручную отредактировать файл публикации (default.vrd) и удалить
там «ненужные» сервисы.

HTTP-запросы
В этом разделе рассказывается о том, как из системы «1С:Предприятие»
можно обратиться к любому произвольному HTTP-сервису, опубликованному
в Интернете, а также как обратиться к REST-интерфейсу любой внешней
системы, доступному по протоколу OData.
Для простоты мы покажем примеры обращения к HTTP-сервисам
и REST-интерфейсу (OData) прикладного решения «1С:Предприятия»,
реализованные нами в той же демонстрационной конфигурации, что и сами
сервисы.
Ниже мы рассмотрим примеры получения, удаления и добавления данных
прикладного решения «1С:Предприятия» с помощью HTTP-сервисов,
разработанных нами в предыдущем разделе «Примеры реализации
HTTP-сервисов». А затем изменение данных прикладного решения через
REST-интерфейс (OData), который будет рассматриваться в следующем
разделе «Автоматический REST-интерфейс (OData)».
ПОДРОБНЕЕ
Подробнее познакомиться с реализацией и использованием HTTP-запросов
можно в демонстрационной конфигурации «Примеры работы», которая
прилагается к книге.

Для обращения к HTTP-сервисам и REST-интерфейсу (OData) используются
такие объекты встроенного языка, как HTTPСоединение, HTTPЗапрос
и HTTPОтвет.

■■ Объект HTTPСоединение предназначен для взаимодействия с внешними системами по протоколу HTTP. В том числе с помощью объекта
HTTPСоединениеможно:
□□ создать HTTP-соединение;
□□ записать, получить и удалить файл;
□□ отправить ресурс на указанный адрес для обработки;

74

Технологии интеграции «1С:Предприятия 8.3»

□□
□□
□□
□□

определить параметры установленного соединения;
вызвать произвольный HTTP-метод;
передать на сервер заголовки запроса и получить заголовки с сервера;
в качестве тела запроса отправить не только файл, но и строку (с автоматическим перекодированием в нужную кодировку), и двоичные
данные.
■■ Объект HTTPЗапрос предназначен для описания HTTP-запросов,
отправляемых через объект HTTPСоединение. Позволяет задать адрес
запрашиваемого ресурса, заголовки и тело запроса.
■■ Объект HTTPОтвет – предоставляет доступ к содержимому ответа HTTPсервера на запрос.
Для того чтобы изменить (добавить, удалить, прочитать и т. д.) данные информационной базы через REST-интерфейс, нужно создать HTTP-соединение
с сервером (например, «localhost»), на котором опубликованы HTTPсервисы (как созданные разработчиком, так и составляющие автоматический
REST-интерфейс) для прикладного решения. Затем создать HTTP-запрос
на основе адреса ресурса данных, которые требуется прочитать или изменить с помощью запроса. После этого нужно записать изменяемые данные
в строку JSON/XML (или в файл, откуда будет считано тело запроса при
отправке) и установить тело HTTP-запроса из этой строки (или установить
имя файла тела запроса). И затем с помощью HTTP-соединения отправить запрос к серверу с соответствующим методом (POST, PUT/PATCH,
DELETE и др.).

Чтобы выполнить HTTP-запрос нужного вида, у объекта HTTPСоединение
существуют методы:
■■
■■
■■
■■
■■
■■

Получить() – отправляет GET-запрос на сервер;
ОтправитьДляОбработки() – отправляет POST-запрос на сервер;
Изменить() – отправляет PATCH-запрос на сервер;
Записать() – отправляет PUT-запрос на сервер;
Удалить() – отправляет DELETE-запрос на сервер;
с помощью метода ВызватьHTTPМетод() можно отправить на сервер
произвольный запрос, выполняющий HTTP-метод, имя которого указано
в первом параметре метода ВызватьHTTPМетод().
Ниже для ознакомления приведен пример POST-запроса в формате atom/
XML, с помощью которого создается новый элемент справочника Товары
(листинг 1.74).

Глава 1. Интернет-технологии

75

Листинг 1.74. POST-запрос в формате atom
POST http://host/base/odata/standard.odata/Catalog_Товары HTTP/1.1
User Agent: Fiddler
Host: host
Content Length: 981



2014 02 14T12:05:55




false
bbb079ae 8c51 11db a9b0 00055d49b45e
false
000000800
Шлепанцы
SL56X
086715b0 f348 11db a9c5 00055d49b45e
Товар

<html>Шлепанцы пляжные</html>




Тот же запрос в формате json выглядит гораздо компактнее (листинг 1.75).
Листинг 1.75. POST-запрос в формате json
POST http://host/base/odata/standard.odata/Catalog_Товары HTTP/1.1
Accept: application/json
Accept Charset: UTF 8
User Agent: Fiddler
Content Type: application/json
Content Length: 2426
{
″DeletionMark″:false,
″Parent_Key″:″bbb079ae 8c51 11db a9b0 00055d49b45e″,
″IsFolder″:false
″Code″:″000000800″,
″Description″:″Шлепанцы″,
″Артикул″:″SL56X″,
″Поставщик_Key″:″086715b0 f348 11db a9c5 00055d49b45e″,
″Вид″:″Товар″,
″Штрихкод″:null,
″Описание:″Шлепанцы пляжные″
}

76

Технологии интеграции «1С:Предприятия 8.3»

Обращение к HTTP-сервисам
Получение данных
Итак, сначала рассмотрим пример получения данных при помощи HTTPзапросов к HTTP-сервисам, разработанных нами в предыдущем разделе.
Для большей универсальности URL, по которому мы обращаемся к HTTPсервису, будем указывать непосредственно в поле ввода демонстрационной
обработки.
Обработчик команды, получающей данные, заполним следующим образом
(листинг 1.76).
Листинг 1.76. Обработчик команды «ПолучитьДанные»
&НаКлиенте
Процедура ПолучитьДанные(Команда)
// Сформировать строку URL.
АдресРесурса = СокрЛП(Адрес);
// Отправить запрос на сервер.
ВыполнитьЗапрос(″GET″, АдресРесурса, ″″);
КонецПроцедуры

В этом обработчике в переменной АдресРесурса мы запоминаем значение
реквизита обработки Адрес, в котором хранится URL для обращения к HTTPсервису.
Затем вызываем процедуру ВыполнитьЗапрос() и передаем в нее
в качестве параметров адрес ресурса и имя метода («GET»), котoрый надо
выполнить на сервере (листинг 1.77).
Листинг 1.77. Процедура «ВыполнитьЗапрос»
&НаКлиенте
Процедура ВыполнитьЗапрос(ИмяМетода, АдресРесурса, СтрокаТелаЗапроса)
Сообщение = Новый СообщениеПользователю;
// Установить имя сервера.
СерверИсточник = ″localhost″;
Попытка

// Создать HTTP-соединение с сервером localhost.

HTTPСоединение = Новый HTTPСоединение(СерверИсточник);
Исключение

// Вывести сообщение об ошибке соединения с сервером.

Сообщение.Текст = ″Не удалось соединиться с сервером: ″ + СерверИсточник;

Сообщение.Сообщить();

Сообщение.Текст = ОписаниеОшибки();

Глава 1. Интернет-технологии

77


Сообщение.Сообщить();

Возврат;
КонецПопытки;
// Создать HTTP-запрос на основе URL.
HTTPЗапрос = Новый HTTPЗапрос(АдресРесурса);
// Установить тело запроса из строки JSON.
Если ИмяМетода ″DELETE″ И ИмяМетода ″GET″ Тогда

HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаТелаЗапроса);
КонецЕсли;
Попытка

// Получить ответ сервера в виде объекта HTTPОтвет.

Результат = HTTPСоединение.ВызватьHTTPМетод(ИмяМетода, HTTPЗапрос);

// Получить содержимое ответа сервера в виде строки.

Сообщение.Текст = Результат.ПолучитьТелоКакСтроку();

Сообщение.Сообщить();
Исключение

// Вывести сообщение об ошибке при получении ответа сервера.

Сообщение.Текст = ОписаниеОшибки();

Сообщение.Сообщить();

Возврат;
КонецПопытки;
КонецПроцедуры

В этой процедуре в качестве имени сервера мы указываем «localhost»,
создаем на его основе объект HTTPСоединение и соединяемся с этим
сервером.
Далее на основе URL, содержащегося в параметре АдресРесурса, создаем
HTTPЗапрос. Затем, в случае если это не DELETE- и не GET-запрос,
устанавливаем тело запроса из строки JSON, содержащейся в параметре
СтрокаТелаЗапроса.

После этого при помощи метода ВызватьHTTPМетод() объекта
HTTPСоединение отправляем на сервер запрос (в данном случае
с методом GET) к информационной базе. Ответ сервера в виде объекта
HTTPОтвет будет возвращен в переменную Результат. C помощью метода
ПолучитьТелоКакСтроку() объекта HTTPОтвет мы получаем тело
ответа в виде строки и выводим его в сообщение.
Проверим, как это работает.
Например, мы хотим получить список всех сотрудников из справочника
Сотрудники. Это можно сделать с помощью HTTP-сервиса Сотрудники
с корневым URL employees.

78

Технологии интеграции «1С:Предприятия 8.3»

В поле ввода демонстрационной обработки укажем тот же URL, который мы
вводили для этого в браузере, – /REST/hs/employees (только без имени
сервера) и нажмем кнопку Получить данные. В результате в окне сообщений
мы увидим ответ сервера, содержащий список всех сотрудников (рис. 1.16).

Рис. 1.16. Ответ сервера

Теперь получим список последних цен товаров из регистра сведений ЦеныТоваров, актуальных на конкретную дату. Это можно сделать с помощью
HTTP-сервиса ЦеныТоваров с корневым URL prices.
В поле ввода обработки укажем тот же URL, который мы вводили для этого
в браузере, – /REST/hs/prices/date/20190701 (только без имени
сервера) и нажмем кнопку Получить данные. В результате в окне сообщений
мы увидим ответ сервера, содержащий список последних цен товаров
(рис. 1.17).

Глава 1. Интернет-технологии

79

Рис. 1.17. Ответ сервера

Удаление данных
С помощью HTTP-сервиса Сотрудники, разработанного нами в предыдущем
разделе «Удалить данные о конкретном сотруднике», а точнее шаблона
сервиса Сотрудник и его HTTP-метода DELETE можно найти сотрудника
в справочнике Сотрудники и поставить ему пометку удаления.
Для проверки введем в справочник нового сотрудника с кодом 000000004.
Затем в поле ввода обработки укажем URL для доступа к сервису –
REST/hs/employees/code/000000004.

Если мы, как и раньше, нажмем кнопку Получить данные, то сработает
HTTP-метод GET шаблона Сотрудник HTTP-сервиса Сотрудники. В результате
на сервер будет отправлен GET-запрос и мы увидим в ответе сервера данные
о только что добавленном сотруднике.
Чтобы отправить на сервер DELETE-запрос, добавим команду УдалитьСотрудника. Обработчик команды заполним следующим образом (листинг 1.78).
Листинг 1.78. Обработчик команды «УдалитьСотрудника»
&НаКлиенте
Процедура УдалитьСотрудника(Команда)
// Сформировать строку URL.
АдресРесурса = СокрЛП(Адрес);
// Отправить запрос на сервер.
ВыполнитьЗапрос(″DELETE″, АдресРесурса, ″″);
КонецПроцедуры

80

Технологии интеграции «1С:Предприятия 8.3»

В этом обработчике мы вызываем процедуру ВыполнитьЗапрос() и передаем в нее в качестве параметров адрес ресурса и имя метода («DELETE»),
котoрый надо выполнить на сервере. Поскольку эта процедура уже была
рассмотрена в листинге 1.77, не будем еще раз на этом останавливаться.
Запустим нашу демонстрационную обработку, укажем URL для доступа
к сервису – REST/hs/employees/code/000000004 и нажмем кнопку
Удалить сотрудника. В результате сработает HTTP-метод DELETE шаблона
Сотрудник HTTP-сервиса Сотрудники. Будет вызван обработчик этого HTTPметода, в котором сотрудник с кодом 000000004 будет найден в справочнике
Сотрудники и помечен на удаление.
Добавление данных
С помощью HTTP-сервиса ЦеныТоваров, разработанного нами в предыдущем
разделе «Проиндексировать цены на товары», а точнее – шаблона сервиса
ПроиндексироватьЦены и его HTTP-метода POST, можно получить последние
цены товаров из регистра сведений ЦеныТоваров, проиндексировать их
на заданную величину и записать в регистр сведений новые цены товаров
на текущую дату, а затем показать эти новые цены в теле ответа, возвращаемого сервисом.
Чтобы отправить на сервер POST-запрос, добавим команду ПроиндексироватьЦены. Обработчик команды заполним следующим образом (листинг 1.79).
Листинг 1.79. Обработчик команды «ПроиндексироватьЦены»
&НаКлиенте
Процедура ПроиндексироватьЦены(Команда)
// Сформировать строку URL.
АдресРесурса = СокрЛП(Адрес);
// Отправить запрос на сервер.
ВыполнитьЗапрос(″POST″, АдресРесурса, ″″);
КонецПроцедуры

В этом обработчике мы вызываем процедуру ВыполнитьЗапрос() и передаем в нее в качестве параметров адрес ресурса и имя метода («POST»),
котoрый надо выполнить на сервере. Поскольку эта процедура уже была
рассмотрена в листинге 1.77, не будем еще раз на этом останавливаться.
Запустим нашу демонстрационную обработку, укажем URL для доступа
к сервису – /REST/hs/prices/percent/10 и нажмем кнопку Повысить
цены.
В результате сработает HTTP-метод POST шаблона ПроиндексироватьЦены
HTTP-сервиса ЦеныТоваров. Будет вызван обработчик этого HTTP-метода,

Глава 1. Интернет-технологии

81

в котором выполняется индексация цен товаров, затем новые цены товаров
будут повышены на 10 процентов и помещены в ответ сервера, а также
в регистр сведений ЦеныТоваров (рис. 1.18).

Рис. 1.18. Ответ сервера и данные регистра сведений «Цены товаров»

Обращение к REST-интерфейсу (OData)
Пример по изменению данных табличной части при помощи HTTP-запроса,
обращающегося к REST-интерфейсу (OData) прикладного решения, рассмотрен ниже в разделе «Примеры использования – Модификация данных
прикладного решения». Также там описаны другие подобные примеры,
позволяющие модифицировать данные, доступные через REST-интерфейс.

Автоматический REST-интерфейс (OData)
Платформа «1С:Предприятие» может автоматически создать REST-интерфейс
для любого прикладного решения. Благодаря своей универсальности и кроссплатформенности REST-интерфейс является очень удобным инструментом
для интеграции прикладного решения со сторонними системами.

Общая информация
Для того чтобы сторонние системы могли обращаться к прикладному
решению через REST-интерфейс, этот интерфейс достаточно опубликовать
на веб-сервере и указать, какие прикладные объекты будут доступны в нем.
После этого внешние приложения с помощью HTTP-запросов через RESTинтерфейс могут читать данные «1С:Предприятия», изменять их, создавать
новые объекты данных и удалять существующие.

82

Технологии интеграции «1С:Предприятия 8.3»

Автоматический REST-интерфейс может использоваться для следующих
задач:
■■ интеграция прикладного решения с интернет-сайтами и интернет-магазинами;
■■ реализация сторонними средствами дополнительной функциональности
прикладного решения без изменения его конфигурации;
■■ загрузка данных в прикладное решение и выгрузка данных из него;
■■ интеграция прикладного решения с корпоративными системами,
возможно, даже без дополнительного программирования.
Доступ к данным приложения через REST-интерфейс позволяет выполнять
следующие типичные операции:
■■ получение списка документов, справочников, записей регистра сведений
и т. п., возможно, с фильтром;
■■ получение данных элемента справочника, документа (по ссылке), данных
записи независимого регистра сведений (по ключу), данных набора
записей подчиненного регистра (по регистратору);
■■ редактирование данных одного элемента справочника, документа
и другого ссылочного объекта;
■■ создание нового элемента справочника, документа, набора записей;
■■ проведение одного документа, старт бизнес-процесса.
В качестве протокола доступа платформа использует протокол OData версии
3.0. Это открытый веб-протокол для запроса и обновления данных. Он позволяет оперировать данными, используя в качестве запросов HTTP-команды.
Получать ответы можно в формате Atom/XML или JSON.
Клиенты OData существуют практически для всех значимых платформ:
■■ мобильные: iOS, Windows Phone, Android;
■■ серверные/настольные: .NET, Java, PHP, Objective-C, Ruby, JavaScript;
■■ поддержка в системах управления содержимым (CMS): Drupal, Joomla.
В этой книге мы не будем рассматривать примеры работы в этих платформах
и то, какими средствами в них выполняется обращение по протоколу OData.
При необходимости вы можете обратиться к документации той платформы,
которая вам нужна.
Поскольку эта книга – о системе «1С:Предприятие», то работу с автоматическим REST-интерфейсом мы рассмотрим для простоты на примере
обращения к нему (клиентская часть) из этой же самой информационной
базы. В реальной жизни такая задача вряд ли встретится, но взаимодействие
одной базы «1С:Предприятия» с другой базой «1С:Предприятия» через
REST-интерфейс – это довольно частая задача. И решается она точно такими
же средствами.

Глава 1. Интернет-технологии

83

Для обращения к REST-интерфейсу используются такие объекты встроенного языка, как HTTPСоединение, HTTPЗапрос и HTTPОтвет (дальше вы
увидите это на примерах в разделе «Примеры использования»).
В REST-интерфейсе доступны практически все основные объекты конфигурации: справочники, документы, константы, перечисления, планы обмена,
регистры накопления, расчета, бухгалтерии и регистры сведений, виртуальные таблицы периодического регистра сведений, регистров бухгалтерии
и регистров накопления, планы счетов, видов характеристик и видов расчета,
бизнес-процессы, задачи и журналы документов.
В REST-интерфейсе доступны также реквизиты объектов конфигурации,
табличные части и реквизиты табличных частей, доступны операции
создания, чтения, модификации и удаления данных, а также некоторые
методы встроенного языка. Например:
■■
■■
■■
■■
■■

для документа – проведение и отмена проведения;
для задачи – выполнение;
для бизнес-процесса – старт;
для регистра сведений – получение среза первых и среза последних;
для регистра накопления и регистра бухгалтерии – получение остатков,
оборотов, остатков и оборотов;
■■ для регистра расчета – получение данных графика, фактического периода
действия, перерасчета и базы.
При чтении и записи данных с помощью REST-интерфейса платформа
выполняет все обычные проверки прав и вызывает обработчики событий.
ПОДРОБНЕЕ
Более подробно про автоматический REST-интерфейс рассказано
в документации «1С:Предприятия» в разделе «Глава 17. Механизмы
интернет-сервисов – HTTP-сервисы – Стандартный интерфейс ODATA».

Правила формирования URL запроса
Обращение к стандартному интерфейсу OData выполняется с помощью
HTTP-запроса по определенному URL. URL формируется специальным
образом и состоит из следующих частей:
1. Адрес информационной базы.
2. Признак обращения к стандартному интерфейсу OData.
3. Имя ресурса, к которому выполняется обращение.
4. Параметры запроса обращения к ресурсу.

84

Технологии интеграции «1С:Предприятия 8.3»

Адрес информационной базы – это обычный URL, по которому выполняется доступ, например, к информационной базе с помощью веб-клиента.
Например, http://localhost/base или http://host.server.zone/
data-base. Также необходимо помнить, что при использовании информационной базы, в которой настроено разделение данных, значения разделителей
можно указывать только в URL информационной базы. Указание разделителей с помощью параметра Z не поддерживается.
Признак обращения к стандартному интерфейсу Odata – это последовательность символов в URL: /odata/standard.odata.

Имя ресурса, к которому выполняется обращение – это особым образом
сформированный идентификатор ресурса (возможно, с параметром) или
предопределенные ресурсы. Например, $metadata или Catalog _
Контрагент(guid’value’).
Параметры обращения к ресурсу. В качестве параметров обращения выступают параметры в виде, принятом для HTTP-запросов:
?ключ=значение&ключ2=значение2.

При обращении к ресурсу могут использоваться специальные ключевые
слова, имеющие специальное назначение:
■■ $format – указывает, в каком формате необходимо получить данные.
Если ключевое слово не указано, данные получаются в формате atom-xml:
□□ $format=atom – возвращает данные в формате atom-xml;
□□ $format=json – возвращает данные в формате json. При обращении
к REST-интерфейсу из других внешних платформ для указания того,
что данные должны возвращаться в формате json, можно указать
MIME-тип application/json в заголовке Accept HTTP-запроса на получение данных.
■■ $metadata – указывает, что требуется получить описание стандартного
интерфейса OData.
■■ $filter – описывает отбор, применяемый при получении данных
(см. раздел «Правила формирования условий отбора»).
■■ $select – описывает перечень свойств сущности (объектных типов,
наборов записей регистров и строк табличных частей объектных типов),
которые нужно получить при обращении к стандартному интерфейсу
OData.
После того как сформирован URL необходимого ресурса, следует выполнить
HTTP-запрос нужного вида. В зависимости от того, какая операция выполняется, используется соответствующий HTTP-метод:
■■ Получение данных – метод GET. В платформе «1С:Предприятие» этому
методу соответствует метод Получить() объекта HTTP-соединение.

Глава 1. Интернет-технологии

85

■■ Создание объекта – метод POST. В платформе «1С:Предприятие» этому
методу соответствует метод ОтправитьДляОбработки() объекта
HTTP-соединение.
■■ Обновление данных:
□□ метод PATCH – в этом случае можно указывать только те свойства
сущности, которые необходимо обновить. В платформе «1С:Предприятие» этому методу соответствует метод Изменить() объекта
HTTP-соединение;
□□ метод PUT – в этом случае необходимо указывать все свойства
сущности. В платформе «1С:Предприятие» этому методу соответствует метод Записать() объекта HTTP-соединение.
■■ Удаление данных – метод DELETE. В платформе «1С:Предприятие» этому
методу соответствует метод Удалить() объекта HTTP-соединение.
В результате выполнения запроса клиентское приложение получает ответ
сервера, который кроме кода состояния может содержать различные данные,
предоставленные сервером, в виде XML- или JSON-документа.
Правила формирования имени ресурса
При обращении к какому-либо ресурсу его идентификатор формируется по следующему принципу: ПрефиксИмени _ ИмяОбъектаКонфигурации _ СуффиксИмени. С помощью стандартного интерфейса OData
можно получить доступ к следующим объектам (табл. 1.1).
Таблица 1.1. Имена ресурсов для объектов конфигурации
Объект конфигурации

Справочник
Документ
Журнал документов
Константа
План обмена
План счетов
План видов расчета
План видов характеристик
Регистр сведений
Регистр накопления
Регистр расчета
Регистр бухгалтерии
Бизнес-процесс
Задача

Префикс имени для указания в URL

Catalog
Document
DocumentJournal
Constant
ExchangePlan
ChartOfAccounts
ChartOfCalculationTypes
ChartOfCharacteristicTypes
InformationRegister
AccumulationRegister
CalculationRegister
AccountingRegister
BusinessProcess
Task

86

Технологии интеграции «1С:Предприятия 8.3»

ИмяОбъектаКонфигурации – имя объекта конфигурации, как оно задано
при разработке прикладного решения в конфигураторе.
СуффиксИмени – предназначен для уточнения имени ресурса и является
необязательной частью имени. В качестве суффикса имени могут выступать
следующие выражения:
■■ имя табличной части объекта;
■■ имя реквизита табличной части или набора записей;
■■ имя виртуальной таблицы регистра;
■■ RowType;
■■ RecordType.
Если объект обладает табличной частью, то для получения доступа ко всем
записям этой табличной части необходимо добавить имя табличной части
после имени самого объекта. Например, для получения всех строк табличной
части Товары всех документов РасходТовара будет необходимо выполнить
GET-запрос по следующему адресу: http://localhost/base/odata/
standard.odata/Document _ РасходТовара _ Товары. Соответствующий пример будет рассмотрен в разделе «Получение данных из табличной
части документа».
Если объект обладает табличной частью, то имеется возможность указать,
что требуется получение не всех реквизитов табличной части, а некоторого
списка этих реквизитов. Для этого необходимо указать в параметре $select
список требуемых реквизитов в следующем виде: /. Аналогичная возможность предоставляется для
наборов записей регистров, где в качестве имени табличной части выступает
RecordSet: RecordSet/.

В роли виртуальной таблицы регистра выступает функция, связанная
с ресурсом, возвращающая набор сущностей регистра. Имя функции совпадает с английским вариантом имени используемой виртуальной таблицы
языка запросов. Параметры функции соответствуют параметрам виртуальной таблицы. Так, для получения среза последних регистра сведений
КурсыВалют следует выполнить GET-запрос по следующему адресу:
h t t p :// l o c a l h o s t / d e m o / o d a t a / s t a n d a r d . o d a t a /
InformationRegister _ КурсыВалют./SliceLast(). Соответствующий
пример будет рассмотрен в разделе «Получение данных регистра сведений».
ПОДРОБНЕЕ
Правила формирования имен ресурсов для получения данных через RESTинтерфейс более подробно описаны в документации «1С:Предприятия»
в разделе «Глава 17. Механизмы интернет-сервисов – HTTP-сервисы –
Стандартный интерфейс ODATA – Правила формирования имени ресурса».

Глава 1. Интернет-технологии

87

Правила формирования условий отбора
При получении данных можно отфильтровать их, если это требуется.
Для этого предназначен специальный язык, позволяющий описывать условия,
которым должны соответствовать данные, которые возвращает стандартный
интерфейс OData. Описание отбора начинается с ключевого слова $filter,
после которого следует собственно условие. Поддерживаются следующие
операции (табл. 1.2, 1.3).
Таблица 1.2. Логические операции, используемые для фильтрации данных
Описание

Имя

Пример

Равно

eq

/Catalog_Города?$filter=Description eq ‘Главный’

Не равно

ne

/Catalog_Города?$filter=Description ne ‘Пермь’

Больше

gt

/Catalog_Товары?$filter=Цена gt 10

Больше или равно

ge

/Catalog_Товары?$filter=Цена ge 10

Меньше

lt

/Catalog_Товары?$filter=Цена lt 10

Меньше или равно

le

/Catalog_Товары?$filter=Цена le 10

Логическое «Или»

or

/Catalog_Товары?$filter=Цена lt 10 or Цена gt 100

Логическое «И»

and

/Catalog_Товары?$filter=Цена gt 10 and Цена lt 100

Отрицание

not

/Catalog_Товары?$filter=not (Цена eq 10)

Таблица 1.3. Арифметические операции, используемые для фильтрации данных
Описание

Имя

Пример

Сложение

add

/Catalog_Товары?$filter=Цена add 5 gt 10

Вычитание

sub

/Catalog_Товары?$filter=Цена sub 5 gt 10

Умножение

mul

/Catalog_Товары?$filter=Цена mul 5 gt 1000

Деление

div

/Catalog_Товары?$filter=Цена div 4 gt 2

Например, используя следующее условие, можно получить товары из справочника Товары с именем Молоко и ценой менее 2500: http://localhost/
odata/standard.odata/Catalog _ Товары?$filter=Имя eq ‘Молоко’ and Цена lt 2500.
Для группировки условий и указания приоритета операций их можно заключить в скобки. Например: http://localhost/odata/standard.odata/
Catalog _ Товары?$filter=(Цена add 5) gt 10.

88

Технологии интеграции «1С:Предприятия 8.3»

При формировании условий запроса или формировании реквизита, по которому выполняется упорядочивание, могут применяться функции для работы
со строками (substring (), startswith(), endswith() и др.), с датами
(year(), dateadd(), dayofyear() и др.) и некоторые другие.
Примеры использования условий отбора получаемых данных будут рассмотрены в разделах «Отобрать записи справочника по условию».
ПОДРОБНЕЕ
Правила формирования условий отбора для получения данных через RESTинтерфейс более подробно описаны в документации «1С:Предприятия»
в разделе «Глава 17. Механизмы интернет-сервисов – HTTP-сервисы –
Стандартный интерфейс ODATA – Правила формирования условия отбора».

Примеры использования
Продемонстрируем все вышесказанное на небольших примерах.
ПОДРОБНЕЕ
Подробнее познакомиться с обращением к данным информационной
базы через REST-интерфейс можно в демонстрационной конфигурации
«Примеры работы», которая прилагается к книге.

Прежде всего, чтобы прикладное решение стало доступно через RESTинтерфейс, опубликуем интерфейс OData для этого решения на веб-сервере.
Для этого в диалоге публикации информационной базы на веб-сервере
должен быть установлен флажок Публиковать стандартный интерфейс OData
(рис. 1.19).
В нашем примере публикация выполняется на локальном компьютере
(на сервере «localhost») в каталог REST.
Затем, для того чтобы объекты конфигурации стали доступны через стандартный интерфейс OData, необходимо разрешить это с помощью метода
глобального контекста УстановитьСоставСтандартногоИнтерфейсаO
Data().

Глава 1. Интернет-технологии

89

Рис. 1.19. Публикация конфигурации на веб-сервере

В реальных примерах для этого нужно написать и один раз выполнить
обработку на встроенном языке, которая укажет, данные каких объектов
конфигурации будут доступны через REST-интерфейс. Но в нашей тренировочной базе мы для простоты с помощью команды сделаем доступными
справочники Поставщики, Должности, Сотрудники и Товары, документ ПоступлениеТоваров и регистр сведений ЦеныТоваров (листинг 1.80).
Листинг 1.80. Установка списка объектов,
доступных через стандартный интерфейс OData
&НаСервереБезКонтекста
Процедура РазрешитьДоступODATA()
МДанные = Новый Массив();
МДанные.Добавить(″Справочник.Сотрудники″);
МДанные.Добавить(″Справочник.Должности″);
МДанные.Добавить(″Справочник.Поставщики″);
МДанные.Добавить(″Документ.ПоступлениеТоваров″);
МДанные.Добавить(″РегистрСведений.ЦеныТоваров″);
МДанные.Добавить(″Справочник.Товары″);
УстановитьСоставСтандартногоИнтерфейсаOData(МДанные);
КонецПроцедуры

90

Технологии интеграции «1С:Предприятия 8.3»

Имена нужных объектов конфигурации мы добавляем в массив и передаем
его в метод УстановитьСоставСтандартногоИнтерфейсаOData().
Следует иметь в виду, что REST-интерфейс по умолчанию возвращает
данные в формате atom-xml. Это «наследие» стандарта. По современным же
представлениям приоритетным форматом является JSON. Поэтому во всех
примерах в списке параметров запроса мы укажем, что данные должны быть
возвращены именно в этом формате – $format=json.
Получение всех данных справочника
В нашей демонстрационной базе существует справочник Поставщики.
Это справочник содержит следующие данные (рис. 1.20).

Рис. 1.20. Данные справочника «Поставщики»

Предположим, нам нужно получить все данные из этого справочника.
Процедура, которая выполняет запрос к данным справочника и получает
ответ в формате JSON, может иметь следующий вид (листинг 1.81).
Листинг 1.81. Пример получения данных из справочника
&НаКлиенте
Процедура Команда1(Команда)
СерверИсточник = ″localhost″;
Сообщение = Новый СообщениеПользователю;
Попытка

// Создать HTTP-соединение с сервером localhost.

HTTPСоединение = Новый HTTPСоединение(СерверИсточник);
Исключение

Глава 1. Интернет-технологии

91


// Вывести сообщение об ошибке соединения с сервером.

Сообщение.Текст = ″Не удалось соединиться с сервером: ″ + СерверИсточник;

Сообщение.Сообщить();

Сообщение.Текст = ОписаниеОшибки();

Сообщение.Сообщить();

Возврат;
КонецПопытки;
// Сформировать строку URL.
АдресРесурса = ″/REST/odata/standard.odata/Catalog_Поставщики?$format=json″;
// Создать HTTP-запрос на основе URL.
HTTPЗапрос = Новый HTTPЗапрос(АдресРесурса);
Попытка

// Получить ответ сервера в виде объекта HTTPОтвет.

Результат = HTTPСоединение.Получить(HTTPЗапрос);

// Получить содержимое ответа сервера в виде строки.

Сообщение.Текст = Результат.ПолучитьТелоКакСтроку();

Сообщение.Сообщить();
Исключение

// Вывести сообщение об ошибке при получении ответа сервера.

Сообщение.Текст = ОписаниеОшибки();

Сообщение.Сообщить();

Возврат;
КонецПопытки;
КонецПроцедуры

В качестве имени сервера мы просто указываем «localhost» и создаем на его
основе объект HTTPСоединение.

Затем в переменной АдресРесурса мы формируем URL запроса для обращения к данным нашего справочника (…/Catalog _ Поставщики?…).
Поскольку нам нужно получить все данные справочника, мы просто указываем для этого префикс Catalog _ и имя справочника.

Остальные составляющие и правила формирования URL были подробно
описаны в разделе «Правила формирования URL запроса».
Далее на основе этого URL создаем HTTPЗапрос и выполняем GET-запрос
к информационной базе при помощи метода Получить() объекта
HTTPСоединение. Ответ сервера в виде объекта HTTPОтвет будет
возвращен в переменную Результат. После этого с помощью метода ПолучитьТелоКакСтроку() объекта HTTPОтвет мы получаем тело ответа
в виде строки и выводим его в сообщение.
Если при получении ответа сервера возникнут ошибки, будет вызвано исключение. Типичные ошибки клиента, возникающие при обращении к серверу,
будут рассмотрены в разделе «Типичные ошибки при получении данных».

92

Технологии интеграции «1С:Предприятия 8.3»

Ответ сервера в виде строки, содержащий все данные справочника Поставщики, будет выглядеть следующим образом (рис. 1.21).

Рис. 1.21. Ответ сервера, содержащий все данные справочника «Поставщики»

Выше, в листинге 1.81, мы показали пример процедуры, с помощью которой
устанавливается HTTP-соединение и через REST-интерфейс получаются
данные информационной базы. Кроме того, аналогичным образом, как будет
показано в следующих примерах, эти данные могут изменяться, удаляться
или добавляться.
Но если нужно просто получить данные информационной базы методом GET,
то можно набрать URL запроса (localhost/REST/odata/standard.
odata/Catalog _ Поставщики?$format=json)
непосредственно
в адресной строке браузера и сразу же в окне браузера увидеть содержимое
ответа сервера (листинг 1.82).
Листинг 1.82. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Catalog_Поставщики″,
″value″: [{
″Ref_Key″: ″9cfabfb0-4cb2-11e9-9334-642737df2048″,
″DataVersion″: ″AAAAAQAAAAA=″,
″DeletionMark″: false,
″Code″: ″000000001″,

Глава 1. Интернет-технологии

93

″Description″: ″ОАО \″Фонтан\″″,
″Predefined″: false,
″PredefinedDataName″: ″″
},{
″Ref_Key″: ″9cfabfb1-4cb2-11e9-9334-642737df2048″,
″DataVersion″: ″AAAAAgAAAAA=″,
″DeletionMark″: false,
″Code″: ″000000002″,
″Description″: ″ООО \″Топаз\″″,
″Predefined″: false,
″PredefinedDataName″: ″″
},{
″Ref_Key″: ″9cfabfb2-4cb2-11e9-9334-642737df2048″,
″DataVersion″: ″AAAAAwAAAAA=″,
″DeletionMark″: false,
″Code″: ″000000003″,
″Description″: ″ООО \″Стиль\″″,
″Predefined″: false,
″PredefinedDataName″: ″″
}]
}

В дальнейших примерах при получении данных из информационной базы
мы будем использовать именно этот метод, то есть смотреть результат
запроса в браузере.
Получение количества записей в результате запроса
Предположим, мы хотим включить в результат запроса не только полученные
записи, но и количество этих записей. В строке URL запроса это задается
с помощью параметра $inlinecount=allpages. Наберем в адресной
строке браузера следующий URL (листинг 1.83).
Листинг 1.83. URL запроса
localhost/REST/odata/standard.odata/Catalog_Поставщики?$format=json&$inlinecount=allpages

ПРИМЕЧАНИЕ
Подобные листинги в этой книге записаны в несколько строк только для
того, чтобы их было удобнее читать. На самом деле URL – это одна строка.

Обратите внимание, что список параметров запроса начинается со знака
вопрос (?), а различные параметры связываются между собой символом
амперсанд (&).
В результате в окне браузера мы увидим содержимое ответа сервера. Количество полученных записей справочника – три (листинг 1.84).

94

Технологии интеграции «1С:Предприятия 8.3»

Листинг 1.84. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Catalog_Поставщики″,
″odata.count″: ″3″,
″value″: [{
″Ref_Key″: ″9cfabfb0-4cb2-11e9-9334-642737df2048″,
″DataVersion″: ″AAAAAQAAAAA=″,
″DeletionMark″: false,
″Code″: ″000000001″,
″Description″: ″ОАО \″Фонтан\″″,
″Predefined″: false,
″PredefinedDataName″: ″″
},{
″Ref_Key″: ″9cfabfb1-4cb2-11e9-9334-642737df2048″,
″DataVersion″: ″AAAAAgAAAAA=″,
″DeletionMark″: false,
″Code″: ″000000002″,
″Description″: ″ООО \″Топаз\″″,
″Predefined″: false,
″PredefinedDataName″: ″″
},{
″Ref_Key″: ″9cfabfb2-4cb2-11e9-9334-642737df2048″,
″DataVersion″: ″AAAAAwAAAAA=″,
″DeletionMark″: false,
″Code″: ″000000003″,
″Description″: ″ООО \″Стиль\″″,
″Predefined″: false,
″PredefinedDataName″: ″″
}]
}

Получение данных из некоторых полей справочника
и их сортировка
В нашей демонстрационной базе существует справочник Должности.
Это справочник содержит следующие данные (рис. 1.22).

Рис. 1.22. Данные справочника «Должности»

Предположим, нам нужно получить не все данные из этого справочника,
а только значения некоторых полей и при этом отсортировать их в определенном порядке.

Глава 1. Интернет-технологии

95

Например, нам нужно получить только значения полей справочника Ref _
Key (ссылка) и Description. Перечень нужных полей в строке URL
запроса задается через запятую в значении параметра $select. Кроме того,
получаемые данные нужно отсортировать по возрастанию кодов должностей.
Это задается с помощью параметра $orderby=Code asc.
Итак, наберем в адресной строке браузера следующий URL (листинг 1.85).
Листинг 1.85. URL запроса
localhost/REST/odata/standard.odata/Catalog_Должности?$format

=json&$select=Ref_Key, Description&$orderby=Code asc

В результате в окне браузера мы увидим содержимое ответа сервера (листинг
1.86).
Листинг 1.86. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Catalog_Должности″,
″value″: [{
″Ref_Key″: ″88d54405-36a1-11e9-8bb2-642737df2048″,
″Description″: ″Менеджер″
},{
″Ref_Key″: ″88d54406-36a1-11e9-8bb2-642737df2048″,
″Description″: ″Администратор″
},{
″Ref_Key″: ″88d54407-36a1-11e9-8bb2-642737df2048″,
″Description″: ″Кассир″
}]
}

Получение данных по ссылке
Теперь получим все данные из справочника Должности для конкретной
должности с известным идентификатором. Этот идентификатор мы можем
скопировать из ответа сервера, полученного в предыдущем примере (см.
листинг 1.86).
Поскольку справочник – это ссылочный объект конфигурации, каждая запись
справочника обладает уникальным идентификатором – ссылкой. Эта ссылка
содержится в поле Ref _ Key. И в предыдущем примере мы как раз получали значение этого поля для всех записей справочника Должности. Кроме
того, когда возвращаются все данные справочника, для каждой записи также
возвращается значение этого поля. Поэтому, получив предыдущий ответ, мы
можем узнать из него Ref _ Key нужного элемента и в следующих запросах
использовать его, указав значение идентификатора в скобках после имени
ресурса – (guid’…’).
Итак, наберем в адресной строке браузера следующий URL (листинг 1.87).

96

Технологии интеграции «1С:Предприятия 8.3»

Листинг 1.87. URL запроса
localhost/REST/odata/standard.odata/Catalog_Должности(
guid’88d54406-36a1-11e9-8bb2-642737df2048’)?$format=json&

В результате в окне браузера мы увидим содержимое ответа сервера (листинг
1.88).
Листинг 1.88. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Catalog_Должности/@Element″,
″Ref_Key″: ″88d54406-36a1-11e9-8bb2-642737df2048″,
″Predefined″: false,
″PredefinedDataName″: ″″,
″DataVersion″: ″AAAAAgAAAAA=″,
″Description″: ″Администратор″,
″Code″: ″000000002″,
″DeletionMark″: false
}

Обратите внимание, что поскольку у справочника ключевое поле одно, то
можно не указывать его имя, а сразу писать в скобках после имени справочника guid’…’. Если же ключевых полей несколько, то имена всех этих полей
вместе с их значениями надо перечислять в скобках после имени ресурса:
(Ref _ Key=guid’…’, LineNumber=…).
Отобрать записи справочника по условию
В строке URL запроса условия отбора получаемых данных задаются в параметре $filter. Более подробно об этом рассказано в разделе «Правила
формирования условий отбора».
Отобрать записи справочника по началу наименования
Предположим, нам требуется отфильтровать записи справочника Поставщики
так, чтобы из списка поставщиков отбирались те, чье наименование начинается на «ООО». Для описания условия отбора воспользуемся функцией
startswith().
Наберем в адресной строке браузера следующий URL (листинг 1.89).
Листинг 1.89. URL запроса
localhost/REST/odata/standard.odata/Catalog_Поставщики?$format=

json&$filter=startswith(Description, ‘ООО’) eq true

Глава 1. Интернет-технологии

97

В результате в ответ сервера будут включены только данные двух из трех
поставщиков, удовлетворяющих условию отбора (листинг 1.90).
Листинг 1.90. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Catalog_Поставщики″,
″value″: [{
″Ref_Key″: ″9cfabfb2-4cb2-11e9-9334-642737df2048″,
″DataVersion″: ″AAAAAwAAAAA=″,
″DeletionMark″: false,
″Code″: ″000000003″,
″Description″: ″ООО \″Стиль\″″,
″Predefined″: false,
″PredefinedDataName″: ″″
},{
″Ref_Key″: ″9cfabfb1-4cb2-11e9-9334-642737df2048″,
″DataVersion″: ″AAAAAgAAAAA=″,
″DeletionMark″: false,
″Code″: ″000000002″,
″Description″: ″ООО \″Топаз\″″,
″Predefined″: false,
″PredefinedDataName″: ″″
}]
}

Отобрать записи справочника по дате рождения
В нашей демонстрационной базе существует справочник Сотрудники. Это
справочник содержит следующие данные (рис. 1.23).

Рис. 1.23. Данные справочника «Сотрудники»

98

Технологии интеграции «1С:Предприятия 8.3»

Предположим, нам требуется отфильтровать записи справочника Сотрудники так, чтобы из списка сотрудников отбирались родившиеся в 1980 году.
Для описания условия отбора воспользуемся функцией year().
Наберем в адресной строке браузера следующий URL (листинг 1.91).
Листинг 1.91. URL запроса
localhost/REST/odata/standard.odata/Catalog_Сотрудники?$format=json&$filter=year(ДатаРождения) eq 1980

В результате в ответ сервера будут включены только данные сотрудников,
удовлетворяющих условию отбора (листинг 1.92).
Листинг 1.92. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Catalog_Сотрудники″,
″value″: [{
″Ref_Key″: ″9575f8ed-369f-11e9-8bb2-642737df2048″,
″DataVersion″: ″AAAAAgAAAAA=″,
″DeletionMark″: false,
″Code″: ″000000001″,
″Description″: ″Алексеев Сергей Иванович″,
″ДатаРождения″: ″1980-12-10T00:00:00″,
″КоличествоДетей″: 1,
″Работает″: true,
″Стаж″: ″10.05.01″,
″Должность_Key″: ″88d54406-36a1-11e9-8bb2-642737df2048″,
″Predefined″: false,
″PredefinedDataName″: ″″,
″Должность@navigationLinkUrl″: ″Catalog_Сотрудники(guid’9575f8ed-369f-11e9-8bb2-642737df2048’)/Должность″
}]
}

Отобрать записи справочника по подстроке
Предположим, нам требуется отфильтровать записи справочника Сотрудники так, чтобы из списка отбирались сотрудники, имеющие в строке стажа
значение 10 месяцев. Для описания условия отбора воспользуемся функцией
substring().
Наберем в адресной строке браузера следующий URL (листинг 1.93).
Листинг 1.93. URL запроса
localhost/REST/odata/standard.odata/Catalog_Сотрудники?$format=json&$filter=substring(Стаж, 4, 2) eq ‘10’

В результате в ответ сервера будут включены только данные сотрудников,
удовлетворяющих условию отбора (листинг 1.94).

Глава 1. Интернет-технологии

99

Листинг 1.94. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Catalog_Сотрудники″,
″value″: [{
″Ref_Key″: ″0b6cd7db-36a0-11e9-8bb2-642737df2048″,
″DataVersion″: ″AAAABAAAAAA=″,
″DeletionMark″: false,
″Code″: ″000000002″,
″Description″: ″Смирнова Светлана Ивановна″,
″ДатаРождения″: ″1990-02-22T00:00:00″,
″КоличествоДетей″: 0,
″Работает″: false,
″Стаж″: ″03.10.27″,
″Должность_Key″: ″88d54407-36a1-11e9-8bb2-642737df2048″,
″Predefined″: false,
″PredefinedDataName″: ″″,
″Должность@navigationLinkUrl″: ″Catalog_Сотрудники(guid’0b6cd7db-36a0-11e9-8bb2-642737df2048’)/Должность″
}]
}

Получение данных связанных сущностей (ссылок)
В листингах 1.92, 1.94 мы видим, что ссылочное поле Должность представлено в виде строкового значения ссылки. Но с помощью параметра $expand
мы можем «развернуть» эти значения. Данный параметр позволяет вместе
с результатами основного запроса получать значения связанных сущностей,
что позволит не запрашивать каждую сущность отдельно.
Для этого наберем в адресной строке браузера следующий URL (листинг
1.95).
Листинг 1.95. URL запроса
localhost/REST/odata/standard.odata/Catalog_Сотрудники?$format=json&$select=Description

, ДатаРождения, Должность&$expand=Должность

В результате в окне браузера мы увидим содержимое ответа сервера (листинг
1.96).
Листинг 1.96. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Catalog_Сотрудники″,
″value″: [{
″Description″: ″Алексеев Сергей Иванович″,
″ДатаРождения″: ″1980-12-10T00:00:00″,
″Должность@navigationLinkUrl″: ″Catalog_Сотрудники(guid’9575f8ed-369f-11e9-8bb2-642737df2048’)/Должность″,
″Должность″: {
″Ref_Key″: ″88d54406-36a1-11e9-8bb2-642737df2048″,
″Predefined″: false,

100

Технологии интеграции «1С:Предприятия 8.3»

″PredefinedDataName″: ″″,
″DataVersion″: ″AAAAAgAAAAA=″,
″Description″: ″Администратор″,
″Code″: ″000000002″,
″DeletionMark″: false
}
},{
″Description″: ″Смирнова Светлана Ивановна″,
″ДатаРождения″: ″1990-02-22T00:00:00″,
″Должность@navigationLinkUrl″: ″Catalog_Сотрудники(guid’0b6cd7db-36a0-11e9-8bb2-642737df2048’)/Должность″,
″Должность″: {
″Ref_Key″: ″88d54407-36a1-11e9-8bb2-642737df2048″,
″Predefined″: false,
″PredefinedDataName″: ″″,
″DataVersion″: ″AAAAAwAAAAA=″,
″Description″: ″Кассир″,
″Code″: ″000000003″,
″DeletionMark″: false
}
},{
″Description″: ″Артемов Игорь Владимирович″,
″ДатаРождения″: ″1970-01-31T00:00:00″,
″Должность@navigationLinkUrl″: ″Catalog_Сотрудники(guid’0b6cd7dc-36a0-11e9-8bb2-642737df2048’)/Должность″,
″Должность″: {
″Ref_Key″: ″88d54405-36a1-11e9-8bb2-642737df2048″,
″Predefined″: false,
″PredefinedDataName″: ″″,
″DataVersion″: ″AAAAAQAAAAA=″,
″Description″: ″Менеджер″,
″Code″: ″000000001″,
″DeletionMark″: false
}
}]
}

Получение данных из табличной части документа
Для получения данных из табличной части документа нужно к имени ресурса
добавить имя табличной части. Например, в нашей демонстрационной базе
существует документПоступлениеТоваров с табличной частью Товары, содержащей список поступивших товаров.
Для того чтобы увидеть только данные табличной части, наберем в адресной
строке браузера следующий URL (листинг 1.97).
Листинг 1.97. URL запроса
localhost/REST/odata/standard.odata/Document_ПоступлениеТоваров_Товары?$format=json

В результате в окне браузера мы увидим содержимое ответа сервера (листинг
1.98).

Глава 1. Интернет-технологии

101

Листинг 1.98. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Document_ПоступлениеТоваров_Товары″,
″value″: [{
″Ref_Key″: ″34c5d79a-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″2″,
″Товар_Key″: ″34c5d798-7636-11e9-88b7-642737df2048″,
″Количество″: 5,
″Цена″: ″2000″,
″Сумма″: ″10000″
},{
″Ref_Key″: ″34c5d79a-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″1″,
″Товар_Key″: ″34c5d799-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″5000″,
″Сумма″: ″15000″
},{
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″1″,
″Товар_Key″: ″34c5d799-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″5500″,
″Сумма″: ″16500″
},{
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″2″,
″Товар_Key″: ″34c5d797-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″3300″,
″Сумма″: ″9900″
},{
″Ref_Key″: ″34c5d79c-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″1″,
″Товар_Key″: ″34c5d797-7636-11e9-88b7-642737df2048″,
″Количество″: 5,
″Цена″: ″3000″,
″Сумма″: ″15000″
},{
″Ref_Key″: ″34c5d79c-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″2″,
″Товар_Key″: ″34c5d798-7636-11e9-88b7-642737df2048″,
″Количество″: 5,
″Цена″: ″2000″,
″Сумма″: ″10000″
}]
}

Как мы видим, в ответе сервера содержатся по две строки табличной части
для каждого из трех документов ПоступлениеТоваров.

102

Технологии интеграции «1С:Предприятия 8.3»

Чтобы получить данные конкретной строки табличной части документа,
нужно иметь в виду, что у нее не одно, а два ключевых поля: ссылка на сам
документ (Ref _ Key) и номер строки табличной части (LineNumber).
И все эти поля, и их значения нужно перечислить через запятую в скобках
после имени ресурса.
Итак, наберем в адресной строке браузера следующий URL (листинг 1.99).
Листинг 1.99. URL запроса
localhost/REST/odata/standard.odata/Document_ПоступлениеТоваров_Товары(
Ref_Key=guid’34c5d79b-7636-11e9-88b7-642737df2048’, LineNumber=2)?$format=json

В результате в ответе сервера будут содержаться данные второй строки
табличной части документа с указанной выше ссылкой (листинг 1.100).
Листинг 1.100. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#\

Document_ПоступлениеТоваров_Товары/@Element″,
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″2″,
″Товар_Key″: ″34c5d797-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″3300″,
″Сумма″: ″9900″
}

Без указания номера строки табличной части никакие данные получены не
будут, а в окне браузера мы увидим сообщение, что запрашиваемая страница
не найдена.
Однако если указать значения ключевых полей с помощью параметра
$filter, то в обоих случаях данные будут получены согласно условию
отбора.
Составим условие отбора на равенство для обоих ключевых полей (листинг
1.101).
Листинг 1.101. URL запроса
localhost/REST/odata/standard.odata/Document_ПоступлениеТоваров_Товары?$format=json&$filter
=Ref_Key eq guid’34c5d79b-7636-11e9-88b7-642737df2048’ and LineNumber eq 2

Мы получим тот же результат, что и раньше, то есть данные второй строки
табличной части конкретного документа (см. листинг 1.100).
Опустим в условии отбора номер строки табличной части (листинг 1.102).

Глава 1. Интернет-технологии

103

Листинг 1.102. URL запроса
localhost/REST/odata/standard.odata/Document_ПоступлениеТоваров_Товары?$format=json&$filter
=Ref_Key eq guid’34c5d79b-7636-11e9-88b7-642737df2048’

Тогда в ответе сервера будут содержаться данные обеих строк табличной
части документа с указанной в условии отбора ссылкой (листинг 1.103).
Листинг 1.103. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Document_ПоступлениеТоваров_Товары″,
″value″: [{
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″1″,
″Товар_Key″: ″34c5d799-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″5500″,
″Сумма″: ″16500″
},{
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″2″,
″Товар_Key″: ″34c5d797-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″3300″,
″Сумма″: ″9900″
}]
}

Условие отбора по реквизитам табличных частей
При получении данных через REST-интерфейс можно не только выполнять
отбор по реквизитам объектов конфигурации и их табличных частей, но
и накладывать условия отбора на элементы коллекций. Для этого необходимо
использовать лямбда-функции. Система поддерживает использование следующих лямбда-функций:
■■ any – применяет логическое выражение к каждому элементу коллекции
и возвращает значение true, если хоть один элемент коллекции удовлетворяет этому условию. Лямбда-функция any без аргументов возвращает
true, если коллекция не пуста;
■■ all – применяет логическое выражение к каждому элементу коллекции
и возвращает значение true, если все элементы коллекции ему удовлетворяют.
Например, нам нужно получить список документов ПоступлениеТоваров,
у которых цена хотя бы одного поступившего товара (значение реквизита
Цена табличной части Товары) больше 3000.

104

Технологии интеграции «1С:Предприятия 8.3»

Для этого наберем в адресной строке браузера следующий URL (листинг
1.104).
Листинг 1.104. URL запроса
localhost/REST/odata/standard.odata/Document_ПоступлениеТоваров?$filter
=Товары/any(d: d/Цена gt 3000)&$format=json

В результате в ответе сервера будут содержаться данные двух из трех документов (Number: 000000001, 000000002), в состав которых входит хотя
бы одна строка табличной части, удовлетворяющая условию (листинг 1.105).
Листинг 1.105. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Document_ПоступлениеТоваров″,
″value″: [{
″Ref_Key″: ″34c5d79a-7636-11e9-88b7-642737df2048″,
″DataVersion″: ″AAAABQAAAAA=″,
″DeletionMark″: false,
″Number″: ″000000001″,
″Date″: ″2019-05-10T12:00:00″,
″Posted″: true,
″Поставщик_Key″: ″9cfabfb1-4cb2-11e9-9334-642737df2048″,
″Товары″: [
{
″Ref_Key″: ″34c5d79a-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″1″,
″Товар_Key″: ″34c5d799-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″5000″,
″Сумма″: ″15000″
},
{
″Ref_Key″: ″34c5d79a-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″2″,
″Товар_Key″: ″34c5d798-7636-11e9-88b7-642737df2048″,
″Количество″: 5,
″Цена″: ″2000″,
″Сумма″: ″10000″
}
],
″Поставщик@navigationLinkUrl″: ″Document_ПоступлениеТоваров(

guid’34c5d79a-7636-11e9-88b7-642737df2048’)/Поставщик″
},{
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″DataVersion″: ″AAAABgAAAAA=″,
″DeletionMark″: false,
″Number″: ″000000002″,
″Date″: ″2019-05-13T12:00:00″,
″Posted″: true,
″Поставщик_Key″: ″9cfabfb0-4cb2-11e9-9334-642737df2048″,
″Товары″: [

Глава 1. Интернет-технологии

105

{
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″1″,
″Товар_Key″: ″34c5d799-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″5500″,
″Сумма″: ″16500″
},
{
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″2″,
″Товар_Key″: ″34c5d797-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″3300″,
″Сумма″: ″9900″
}
],
″Поставщик@navigationLinkUrl″: ″Document_ПоступлениеТоваров(

guid’34c5d79b-7636-11e9-88b7-642737df2048’)/Поставщик″
}]
}

А теперь предположим, что нам нужно получить список документов ПоступлениеТоваров, у которых цена каждого поступившего товара (значение
реквизита Цена табличной части Товары) больше 3000.
Для этого наберем в адресной строке браузера следующий URL (листинг
1.106).
Листинг 1.106. URL запроса
localhost/REST/odata/standard.odata/Document_ПоступлениеТоваров?$filter
=Товары/all(d: d/Цена gt 3000)&$format=json

В результате в ответе сервера будут содержаться данные только одного документа (Number: 000000002), в составе которого все строки табличной
части удовлетворяют заданному условию (листинг 1.107).
Листинг 1.107. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Document_ПоступлениеТоваров″,
″value″: [{
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″DataVersion″: ″AAAABgAAAAA=″,
″DeletionMark″: false,
″Number″: ″000000002″,
″Date″: ″2019-05-13T12:00:00″,
″Posted″: true,
″Поставщик_Key″: ″9cfabfb0-4cb2-11e9-9334-642737df2048″,
″Товары″: [
{

106

Технологии интеграции «1С:Предприятия 8.3»

″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″1″,
″Товар_Key″: ″34c5d799-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″5500″,
″Сумма″: ″16500″
},
{
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″2″,
″Товар_Key″: ″34c5d797-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″3300″,
″Сумма″: ″9900″
}
],
″Поставщик@navigationLinkUrl″: ″Document_ПоступлениеТоваров(

guid’34c5d79b-7636-11e9-88b7-642737df2048’)/Поставщик″
}]
}

Для сравнения: если мы наложим условие непосредственно на значение
реквизита Цена табличной части Товары (Товары/Цена), то получим только
те строки табличной части, которые удовлетворяют заданному условию
(листинг 1.108, 1.109).
Листинг 1.108. URL запроса
localhost/REST/odata/standard.odata/Document_ПоступлениеТоваров?$filter
=Товары/Цена gt 3000&$format=json
Листинг 1.109. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#Document_ПоступлениеТоваров″,
″value″: [{
″Ref_Key″: ″34c5d79a-7636-11e9-88b7-642737df2048″,
″DataVersion″: ″AAAABQAAAAA=″,
″DeletionMark″: false,
″Number″: ″000000001″,
″Date″: ″2019-05-10T12:00:00″,
″Posted″: true,
″Поставщик_Key″: ″9cfabfb1-4cb2-11e9-9334-642737df2048″,
″Товары″: [
{
″Ref_Key″: ″34c5d79a-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″1″,
″Товар_Key″: ″34c5d799-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″5000″,
″Сумма″: ″15000″
}

Глава 1. Интернет-технологии

107

],
″Поставщик@navigationLinkUrl″: ″Document_ПоступлениеТоваров(

guid’34c5d79a-7636-11e9-88b7-642737df2048’)/Поставщик″
},{
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″DataVersion″: ″AAAABgAAAAA=″,
″DeletionMark″: false,
″Number″: ″000000002″,
″Date″: ″2019-05-13T12:00:00″,
″Posted″: true,
″Поставщик_Key″: ″9cfabfb0-4cb2-11e9-9334-642737df2048″,
″Товары″: [
{
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″1″,
″Товар_Key″: ″34c5d799-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″5500″,
″Сумма″: ″16500″
},
{
″Ref_Key″: ″34c5d79b-7636-11e9-88b7-642737df2048″,
″LineNumber″: ″2″,
″Товар_Key″: ″34c5d797-7636-11e9-88b7-642737df2048″,
″Количество″: 3,
″Цена″: ″3300″,
″Сумма″: ″9900″
}
],
″Поставщик@navigationLinkUrl″: ″Document_ПоступлениеТоваров(

guid’34c5d79b-7636-11e9-88b7-642737df2048’)/Поставщик″
}]
}

Получение данных регистра сведений
В нашей демонстрационной базе существует регистр сведений ЦеныТоваров.
Этот регистр сведений содержит следующие данные (рис. 1.24).

Рис. 1.24. Данные регистра сведений «ЦеныТоваров»

108

Технологии интеграции «1С:Предприятия 8.3»

Предположим, нам требуется получить срез последних записей регистра
сведений ЦеныТоваров, то есть получить последние актуальные цены товаров
на текущую дату. Для этого воспользуемся функцией SliceLast().
Наберем в адресной строке браузера следующий URL (листинг 1.110).
Листинг 1.110. URL запроса
localhost/REST/odata/standard.odata/InformationRegister_ЦеныТоваров/SliceLast()
?$format=json&$expand=Товар&$select=Period, Товар/Description, Цена

Для наглядности мы получили связанные данные из справочника товаров
с помощью параметра $expand и вывели название товара (Товар/
Description) в списке полей.
В результате в окне браузера мы увидим содержимое ответа сервера (листинг
1.111).
Листинг 1.111. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#InformationRegister_ЦеныТоваров″,
″value″: [{
″Period″: ″2019-07-21T00:00:00″,
″Цена″: 3630,
″Товар@navigationLinkUrl″: ″InformationRegister_ЦеныТоваров(
Period=datetime’2019-07-21T00:00:00’, Товар_Key=guid’34c5d797-7636-11e9-88b7-642737df2048’)/Товар″,
″Товар″: {
″Description″: ″Чайник″
}
},{
″Period″: ″2019-07-21T00:00:00″,
″Цена″: 6050,
″Товар@navigationLinkUrl″: ″InformationRegister_ЦеныТоваров(
Period=datetime’2019-07-21T00:00:00’, Товар_Key=guid’34c5d799-7636-11e9-88b7-642737df2048’)/Товар″,
″Товар″: {
″Description″: ″Кофемашина″
}
},{
″Period″: ″2019-07-21T00:00:00″,
″Цена″: 2420,
″Товар@navigationLinkUrl″: ″InformationRegister_ЦеныТоваров(
Period=datetime’2019-07-21T00:00:00’, Товар_Key=guid’34c5d798-7636-11e9-88b7-642737df2048’)/Товар″,
″Товар″: {
″Description″: ″Тостер″
}
}]
}

Глава 1. Интернет-технологии

109

Теперь для закрепления ранее полученных знаний выведем все записи регистра сведений для конкретного товара (кофемашина), при этом историю
назначения цен на товар расположим в порядке убывания дат. Для этого
отфильтруем записи регистра сведений по измерению Товар (ключевое поле
Товар _ Key).
Наберем в адресной строке браузера следующий URL (листинг 1.112).
Листинг 1.112. URL запроса
localhost/REST/odata/standard.odata/InformationRegister_ЦеныТоваров?$filter
=Товар_Key eq guid’34c5d799-7636-11e9-88b7-642737df2048’&$format=json&$expand

=Товар&$select=Period, Товар/Description, Цена&$orderby=Period desc

В результате в окне браузера мы увидим содержимое ответа сервера (листинг
1.113).
Листинг 1.113. Содержимое ответа сервера
{
″odata.metadata″: ″http://localhost/REST/odata/standard.odata/$metadata#InformationRegister_ЦеныТоваров″,
″value″: [{
″Period″: ″2019-07-21T00:00:00″,
″Цена″: 6050,
″Товар@navigationLinkUrl″: ″InformationRegister_ЦеныТоваров(
Period=datetime’2019-07-21T00:00:00’, Товар_Key=guid’34c5d799-7636-11e9-88b7-642737df2048’)/Товар″,
″Товар″: {
″Description″: ″Кофемашина″
}
},{
″Period″: ″2019-05-13T00:00:00″,
″Цена″: 5500,
″Товар@navigationLinkUrl″: ″InformationRegister_ЦеныТоваров(
Period=datetime’2019-05-13T00:00:00’, Товар_Key=guid’34c5d799-7636-11e9-88b7-642737df2048’)/Товар″,
″Товар″: {
″Description″: ″Кофемашина″
}
},{
″Period″: ″2019-05-07T00:00:00″,
″Цена″: 5000,
″Товар@navigationLinkUrl″: ″InformationRegister_ЦеныТоваров(
Period=datetime’2019-05-07T00:00:00’, Товар_Key=guid’34c5d799-7636-11e9-88b7-642737df2048’)/Товар″,
″Товар″: {
″Description″: ″Кофемашина″
}
}]
}

110

Технологии интеграции «1С:Предприятия 8.3»

Модификация данных прикладного решения
До сих пор мы рассматривали примеры получения данных через RESTинтерфейс, то есть чтения данных с сервера методом GET. Теперь мы
покажем, как можно изменять, добавлять и удалять данные информационной
базы.
Как уже говорилось, запросы на чтение данных можно выполнять непосредственно в браузере. Остальные действия, модифицирующие данные
информационной базы, не могут быть выполнены подобным образом.
Для этого нужно записать изменяемые или добавляемые данные в строку
JSON, затем установить HTTP-соединение с сервером, на котором опубликован интерфейс OData для прикладного решения. После этого создать
HTTP-запрос на основе URL модифицируемых данных, установить тело
HTTP-запроса из строки JSON и отправить запрос к серверу с соответствующим методом (POST, PUT/PATCH, DELETE).
Поясним вышесказанное на небольших примерах.
Добавление данных
Предположим, нам нужно добавить новую запись в справочник Должности.
Обработчик команды, добавляющей данные, заполним следующим образом
(листинг 1.114).
Листинг 1.114. Обработчик команды «ДобавитьДанные»
&НаКлиенте
Процедура ДобавитьДанные(Команда)
Сообщение = Новый СообщениеПользователю;
// Сформировать строку URL.
АдресРесурса = ″/REST/odata/standard.odata/Catalog_Должности?$format=json″;
// Получить строку JSON с добавляемыми данными.
СтрокаТелаЗапроса = СформироватьДобавляемыеДанные();
Сообщение.Текст = СтрокаТелаЗапроса;
Сообщение.Сообщить();
// Отправить запрос на сервер.
ВыполнитьЗапрос(″POST″, АдресРесурса, СтрокаТелаЗапроса);
КонецПроцедуры

В этом обработчике в переменной АдресРесурса мы формируем URL
запроса для обращения к данным нашего справочника.

Глава 1. Интернет-технологии

111

Затем с помощью функции СформироватьДобавляемыеДанные(),
которая будет рассмотрена ниже, в листинге 1.116, мы записываем
добавляемые данные в строку JSON и возвращаем ее в переменную СтрокаТелаЗапроса.

После этого вызываем процедуру ВыполнитьЗапрос() и передаем в нее
в качестве параметров адрес ресурса, строку тела запроса и имя метода
(«POST»), котoрый надо выполнить на сервере (листинг 1.115).
Листинг 1.115. Процедура «ВыполнитьЗапрос»
&НаКлиенте
Процедура ВыполнитьЗапрос(ИмяМетода, АдресРесурса, СтрокаТелаЗапроса)
Сообщение = Новый СообщениеПользователю;
// Установить имя сервера.
СерверИсточник = ″localhost″;
Попытка

// Создать HTTP-соединение с сервером localhost.

HTTPСоединение = Новый HTTPСоединение(СерверИсточник);
Исключение

// Вывести сообщение об ошибке соединения с сервером.

Сообщение.Текст = ″Не удалось соединиться с сервером: ″ + СерверИсточник;

Сообщение.Сообщить();

Сообщение.Текст = ОписаниеОшибки();

Сообщение.Сообщить();

Возврат;
КонецПопытки;
// Создать HTTP-запрос на основе URL.
HTTPЗапрос = Новый HTTPЗапрос(АдресРесурса);
// Установить тело запроса из строки JSON.
Если ИмяМетода ″DELETE″ И ИмяМетода ″GET″ Тогда

HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаТелаЗапроса);
КонецЕсли;
Попытка

// Получить ответ сервера в виде объекта HTTPОтвет.

Результат = HTTPСоединение.ВызватьHTTPМетод(ИмяМетода, HTTPЗапрос);

//получить содержимое ответа сервера в виде строки

Сообщение.Текст = Результат.ПолучитьТелоКакСтроку();

Сообщение.Сообщить();
Исключение

// Вывести сообщение об ошибке при получении ответа сервера.

Сообщение.Текст = ОписаниеОшибки();

Сообщение.Сообщить();

Возврат;
КонецПопытки;
КонецПроцедуры

112

Технологии интеграции «1С:Предприятия 8.3»

В этой процедуре в качестве имени сервера мы указываем «localhost»,
создаем на его основе объект HTTPСоединение и соединяемся с этим
сервером.
Далее на основе URL, содержащегося в параметре АдресРесурса, создаем
HTTPЗапрос. Затем, в случае если это не DELETE- и не GET-запрос,
устанавливаем тело запроса из строки JSON, содержащейся в параметре
СтрокаТелаЗапроса.
После этого при помощи метода ВызватьHTTPМетод() объекта
HTTPСоединение отправляем на сервер POST-запрос к информационной
базе. Ответ сервера в виде объекта HTTPОтвет будет возвращен в переменную Результат. C помощью метода ПолучитьТелоКакСтроку()
объекта HTTPОтвет мы получаем тело ответа в виде строки и выводим его
в сообщение.
В результате в справочник будут добавлены новые данные, записанные
в строку JSON в функции СформироватьДобавляемыеДанные().
Заполним эту функцию следующим образом (листинг 1.116).
Листинг 1.116. Функция «СформироватьДобавляемыеДанные»
&НаСервереБезКонтекста
Функция СформироватьДобавляемыеДанные()
// Создать объект записи и записать строковое значение в строку JSON.
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
// Записать начало корневого объекта.
Запись.ЗаписатьНачалоОбъекта();
// Записать наименование новой должности.
Запись.ЗаписатьИмяСвойства(″Description″);
Запись.ЗаписатьЗначение(″Новая должность - REST″);
// Записать конец корневого объекта.
Запись.ЗаписатьКонецОбъекта();
СтрокаJSON = Запись.Закрыть();

Возврат СтрокаJSON;
КонецФункции

В этой функции методом потоковой записи с помощью объекта ЗаписьJSON
мы записываем в строку JSON наименование (свойство Description)
новой должности, которую мы хотим добавить в справочник Должности.

Глава 1. Интернет-технологии

113

В результате при выполнении команды по добавлению данных на сервер
будет отправлен POST-запрос к информационной базе. Ответ сервера в виде
строки будет содержать данные нового элемента, добавленного в справочник
(рис. 1.25).

Рис. 1.25. Ответ сервера

Таким образом, содержимое справочника теперь выглядит следующим
образом (рис. 1.26).

Рис. 1.26. Данные справочника «Должности»

114

Технологии интеграции «1С:Предприятия 8.3»

Изменение данных
Теперь предположим, что нам нужно изменить некоторые поля (а именно:
Код, ДатаРождения и Должность) у конкретного элемента справочника
Сотрудники. Поскольку мы хотим изменить не все, а только часть полей
записи справочника, для этого нужно обратиться к элементу справочника
с конкретной ссылкой и отправить на сервер PATCH-запрос с изменяемыми
данными.
Обработчик команды, изменяющей данные, заполним следующим образом
(листинг 1.117).
Листинг 1.117. Обработчик команды «ИзменитьДанные»
&НаКлиенте
Процедура ИзменитьДанные(Команда)
Сообщение = Новый СообщениеПользователю;
// Сформировать строку URL.
АдресРесурса = ″/REST/odata/standard.odata/Catalog_Сотрудники(

guid’0b6cd7dc-36a0-11e9-8bb2-642737df2048’)?$format=json″;
// Получить строку JSON с изменяемыми данными.
СтрокаТелаЗапроса = СформироватьИзменяемыеДанные();
Сообщение.Текст = СтрокаТелаЗапроса;
Сообщение.Сообщить();
// Отправить запрос на сервер.
ВыполнитьЗапрос(″PATCH″, АдресРесурса, СтрокаТелаЗапроса);
КонецПроцедуры

В этом обработчике в переменной АдресРесурса мы формируем URL
запроса для обращения к данным конкретного элемента нашего справочника.
В скобках мы указываем ссылку на элемент, которую мы для простоты копируем из рассмотренных выше примеров чтения данных из этого справочника.
Затем с помощью функции СформироватьИзменяемыеДанные(), которая
будет рассмотрена ниже, в листинге 1.118, мы записываем изменяемые
данные в строку JSON и возвращаем ее в переменную СтрокаТелаЗапроса.
После этого вызываем процедуру ВыполнитьЗапрос() и передаем в нее
в качестве параметров адрес ресурса, строку тела запроса и имя метода
(«PATCH»), котoрый надо выполнить на сервере. Поскольку эта процедура
уже была рассмотрена в листинге 1.115, не будем еще раз на этом останавливаться.

Глава 1. Интернет-технологии

115

В результате у конкретного сотрудника (Артемов Игорь Владимирович) будут
изменены те данные, которые мы запишем в строку JSON в функции СформироватьИзменяемыеДанные() следующим образом (листинг 1.118).
Листинг 1.118. Функция «СформироватьИзменяемыеДанные»
&НаСервереБезКонтекста
Функция СформироватьИзменяемыеДанные()
// Создать объект записи и записать строковое значение в строку JSON.
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
// Записать начало корневого объекта.
Запись.ЗаписатьНачалоОбъекта();
// Изменить свойство Code.
Запись.ЗаписатьИмяСвойства(″Code″);
Запись.ЗаписатьЗначение(″REST-0003″);
// Изменить свойство ДатаРождения.
Запись.ЗаписатьИмяСвойства(″ДатаРождения″);
Запись.ЗаписатьЗначение(ЗаписатьДатуJSON(ТекущаяДата()

, ФорматДатыJSON.ISO, ВариантЗаписиДатыJSON.УниверсальнаяДата));
// Изменить свойство ссылочное поле Должность.
Запись.ЗаписатьИмяСвойства(″Должность@odata.bind″);
Запись.ЗаписатьЗначение(″Catalog_Должности(guid’524ffe8a-789f-11e9-babb-642737df2048’)″);
// Записать конец корневого объекта.
Запись.ЗаписатьКонецОбъекта();
СтрокаJSON = Запись.Закрыть();

Возврат СтрокаJSON;
КонецФункции

В этой функции методом потоковой записи с помощью объекта ЗаписьJSON
мы записываем в строку JSON значения полей Код (свойство Code),
ДатаРождения и Должность, которые мы хотим изменить у конкретного
элемента справочника Сотрудники.
Код – это строковое поле, поэтому в свойство Code мы просто записываем строку «REST-0003». В поле ДатаРождения с помощью функции

ЗаписатьДатуJSON() мы записываем текущую дату как универсальную
дату в формате ISO. Поле Должность – это ссылочное поле, которое ссылается на справочник Должности, поэтому мы указываем для него имя
свойства как "Должность@odata.bind", а значение – "Catalog _
Должности(guid’524ffe8a-789f-11e9-babb-642737df2048’)".

116

Технологии интеграции «1С:Предприятия 8.3»

Значение уникального идентификатора этой должности мы для простоты
копируем из предыдущего примера (см. рис. 1.25), когда мы добавляли новую
запись в справочник Должности. Таким образом, наименование должности
у сотрудника теперь изменится с Менеджер на Новая должность – REST (см.
рис. 1.28).
В результате при выполнении команды по изменению данных на сервер
будет отправлен PATCH-запрос к информационной базе. Ответ сервера
в виде строки будет содержать данные измененного элемента справочника
(рис. 1.27).

Рис. 1.27. Ответ сервера

Таким образом, содержимое справочника теперь выглядит следующим
образом (рис. 1.28).

Рис. 1.28. Данные справочника «Сотрудники»

Глава 1. Интернет-технологии

117

Удаление данных
Теперь предположим, что нам нужно удалить конкретную запись из справочника Должности. Для этого нужно обратиться к элементу справочника
с конкретной ссылкой и отправить на сервер DELETE-запрос.
Обработчик команды, удаляющей данные, заполним следующим образом
(листинг 1.119).
Листинг 1.119. Обработчик команды «УдалитьДанные»
&НаКлиенте
Процедура УдалитьДанные(Команда)
// Сформировать строку URL.
АдресРесурса = ″/REST/odata/standard.odata/Catalog_Должности(

guid’4f158949-78ab-11e9-babb-642737df2048’)?$format=json″;
// Отправить запрос на сервер.
ВыполнитьЗапрос(″DELETE″, АдресРесурса, ″″);
КонецПроцедуры

В этом обработчике в переменной АдресРесурса мы формируем URL
запроса для обращения к данным конкретного элемента нашего справочника. Чтобы получить ссылку на элемент справочника Должности, который
необходимо удалить, выполним еще раз команду Добавить данные. Затем
из тела ответа сервера скопируем ссылку на добавленную должность
и вставим ее в адрес ресурса.
После этого вызываем процедуру ВыполнитьЗапрос() и передаем в нее
в качестве параметров адрес ресурса и имя метода («DELETE»), котoрый
надо выполнить на сервере. Поскольку эта процедура уже была рассмотрена
в листинге 1.115, не будем еще раз на этом останавливаться. Подчеркнем
только, что для DELETE- и GET-запросов тело запроса устанавливать не
нужно, поэтому третьим параметром мы передаем в процедуру пустую
строку.
В результате запись с указанным идентификатором будет удалена из справочника Должности, а тело ответа сервера будет пустым.
Изменение данных табличной части
В заключение покажем, как изменять данные табличных частей.
Предположим, нам нужно изменить документ ПоступлениеТоваров, включая
его табличную часть. В самом документе мы изменим номер и дату.
Для этого нам нужно отправить на сервер PATCH-запрос к конкретному

118

Технологии интеграции «1С:Предприятия 8.3»

документу. Однако данные табличной части нужно перезаписывать полностью, несмотря на то что мы хотели изменить только некоторые поля или
строки этой табличной части.
Обработчик команды, изменяющей данные, заполним следующим образом
(листинг 1.120).
Листинг 1.120. Обработчик команды «ЗаписатьДанные»
&НаКлиенте
Процедура ЗаписатьДанные(Команда)
Сообщение = Новый СообщениеПользователю;
// Сформировать строку URL.
АдресРесурса = ″/REST/odata/standard.odata/Document_ПоступлениеТоваров(

guid’34c5d79c-7636-11e9-88b7-642737df2048’)?$format=json″;
// Получить строку JSON с изменяемыми данными.
СтрокаТелаЗапроса = СформироватьИзменяемыеДанныеТЧ();
// Отправить запрос на сервер.
ВыполнитьЗапрос(″PATCH″, АдресРесурса, СтрокаТелаЗапроса);
КонецПроцедуры

В этом обработчике в переменной АдресРесурса мы формируем URL,
на основе которого будет создан HTTP-запрос для обращения к данным
конкретной строки документа, которую мы хотим изменить. О правилах
формирования URL ресурсов, доступ к которым осуществляется через RESTинтерфейс, более подробно рассказывается в разделе «Автоматический
REST-интерфейс – Правила формирования URL запроса».
Затем с помощью функции СформироватьИзменяемыеДанныеТЧ(),
которая будет рассмотрена ниже, в листинге 1.121, мы записываем
изменяемые данные в строку JSON и возвращаем ее в переменную СтрокаТелаЗапроса.

В этом обработчике мы вызываем процедуру ВыполнитьЗапрос() и передаем в нее в качестве параметров адрес ресурса, строку тела запроса и имя
метода («PATCH»), котoрый надо выполнить на сервере. Поскольку эта
процедура уже была рассмотрена в листинге 1.115, не будем еще раз на этом
останавливаться.
В результате у конкретного документа ПоступлениеТоваров будут изменены те
данные, которые мы запишем в строку JSON в функции СформироватьИзменяемыеДанныеТЧ() следующим образом (листинг 1.121).

Глава 1. Интернет-технологии
Листинг 1.121. Функция «СформироватьИзменяемыеДанныеТЧ»
&НаСервереБезКонтекста
Функция СформироватьИзменяемыеДанныеТЧ()
// Создать объект записи и записать строковое значение в строку JSON.
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
// Записать начало корневого объекта.
Запись.ЗаписатьНачалоОбъекта();
// Изменить свойство Номер.
Запись.ЗаписатьИмяСвойства(″Number″);
Запись.ЗаписатьЗначение(″000000777″);
// Изменить свойство Дата.
Запись.ЗаписатьИмяСвойства(″Date″);
Запись.ЗаписатьЗначение(ЗаписатьДатуJSON(ТекущаяДата()

, ФорматДатыJSON.ISO, ВариантЗаписиДатыJSON.УниверсальнаяДата));
// Изменить состав ТЧ Товары.
Запись.ЗаписатьИмяСвойства(″Товары@odata.type″);
Запись.ЗаписатьЗначение(″Collection(

StandardODATA.Document_ПоступлениеТоваров_Товары_RowType)″);
Запись.ЗаписатьИмяСвойства(″Товары″);
Запись.ЗаписатьНачалоМассива();
// записать полностью первую строку ТЧ.
Запись.ЗаписатьНачалоОбъекта();
Запись.ЗаписатьИмяСвойства(″LineNumber″);
Запись.ЗаписатьЗначение(″1″);
Запись.ЗаписатьИмяСвойства(″Количество″);
Запись.ЗаписатьЗначение(″1″);
Запись.ЗаписатьИмяСвойства(″Цена″);
Запись.ЗаписатьЗначение(″10000″);
Запись.ЗаписатьИмяСвойства(″Сумма″);
Запись.ЗаписатьЗначение(″10000″);
Запись.ЗаписатьИмяСвойства(″Товар_Key″);
Запись.ЗаписатьЗначение(″34c5d799-7636-11e9-88b7-642737df2048″);
Запись.ЗаписатьКонецОбъекта();
// записать полностью вторую строку ТЧ.
Запись.ЗаписатьНачалоОбъекта();
Запись.ЗаписатьИмяСвойства(″LineNumber″);
Запись.ЗаписатьЗначение(″2″);
Запись.ЗаписатьИмяСвойства(″Количество″);
Запись.ЗаписатьЗначение(″2″);
Запись.ЗаписатьИмяСвойства(″Цена″);
Запись.ЗаписатьЗначение(″5000″);
Запись.ЗаписатьИмяСвойства(″Сумма″);
Запись.ЗаписатьЗначение(″10000″);
Запись.ЗаписатьИмяСвойства(″Товар_Key″);
Запись.ЗаписатьЗначение(″34c5d798-7636-11e9-88b7-642737df2048″);

119

120

Технологии интеграции «1С:Предприятия 8.3»

Запись.ЗаписатьКонецОбъекта();
Запись.ЗаписатьКонецМассива();
// Записать конец корневого объекта.
Запись.ЗаписатьКонецОбъекта();
СтрокаJSON = Запись.Закрыть();

Возврат СтрокаJSON;
КонецФункции

В этой функции методом потоковой записи с помощью объекта ЗаписьJSON
мы записываем в строку JSON значения полей Номер (свойство Number),
Дата (свойство Date) и все поля и строки табличной части Товары, которые
мы хотим изменить у конкретного документа ПоступлениеТоваров.
Номер – это строковое поле, поэтому в свойство Number мы просто записываем строку «000000777». В поле Дата (свойство Date) с помощью функции

ЗаписатьДатуJSON() мы записываем текущую дату как универсальную
дату в формате ISO.

Поскольку мы собираемся изменять состав табличной части Товары, мы
указываем имя свойства этой коллекции значений как "Товары@odata.
type", а значение – "Collection(StandardODATA.Document _
ПоступлениеТоваров _ Товары _ RowType)".
Затем
записываем
в свойство Товары массив, каждый элемент которого содержит объект, полностью описывающий каждую строку табличной части.
В результате при выполнении команды по изменению данных на сервер будет
отправлен PATCH-запрос к информационной базе. Ответ сервера в виде
строки будет содержать данные измененного документа (рис. 1.29).
Таким образом, содержимое документа будет выглядеть следующим образом
(рис. 1.30).

Глава 1. Интернет-технологии

121

Рис. 1.29. Ответ сервера

Рис. 1.30. Данные измененного документа «Поступление товаров»

Типичные ошибки при получении данных
В этом разделе мы рассмотрим типичные ошибки, возникающие при обращении к REST-интерфейсу прикладного решения. Ошибки могут возникать
как на стороне клиентского приложения, так и на стороне сервера. В случае
возникновения ошибки на клиенте сервер пытается уточнить причину
ошибки и может передать клиентскому приложению дополнительный
внутренний код ошибки и информационное сообщение в теле ответа.
В первую очередь нужно понять, работает ли вообще REST-интерфейс
с интересующей нас информационной базой. Для этого достаточно в браузере
просто набрать URL: /odata/standard.
odata. Должен вернуться следующий ответ сервера (листинг 1.122).

122

Технологии интеграции «1С:Предприятия 8.3»

Листинг 1.122. Ответ сервера


Default



Этот ответ говорит о том, что интерфейс работает, но для интерфейса OData
недоступны никакие объекты конфигурации.
Ниже мы рассмотрим несколько типичных ошибок, которые возникают
в случае неправильного написания URL.
Например, при указании адреса ресурса вы ошиблись в имени объекта
конфигурации (листинг 1.123).
Листинг 1.123. URL запроса
localhost/REST/odata/standard.odata/CatalogДолжности?$format=json

В ответе сервера будет возвращен код ошибки – 8 и описание ошибки – «Тип
сущности не найден» (листинг 1.124).
Листинг 1.124. Ответ сервера
{
″odata.error″: {
″code″: ″8″,
″message″: {
″lang″: ″ru″,
″value″: ″\″CatalogПоставщики\″ entity is not found″
}
}
}

Если при указании адреса ресурса вы ошиблись в значении ссылки
на конкретный объект конфигурации (листинг 1.125).
Листинг 1.125. URL запроса
localhost/REST/odata/standard.odata/Catalog_Должности(
guid’88d54406-36a1-11e9-8bb2-642737df2049’)?$format=json&

В ответе сервера будет возвращен код ошибки – 9 и описание ошибки –
«Экземпляр сущности не найден» (листинг 1.126).

Глава 1. Интернет-технологии

123

Листинг 1.126. Ответ сервера
{
″odata.error″: {
″code″: ″9″,
″message″: {
″lang″: ″ru″,
″value″: ″Entity instance not found″
}
}
}

Если при указании выражения фильтра вы ошиблись, например, при написании функции отбора (листинг 1.127).
Листинг 1.127. URL запроса
localhost/REST/odata/standard.odata/Catalog_Поставщики?$format=json&$filter=startwith(

Description, ‘ООО’) eq true

В ответе сервера будет возвращен код ошибки – 14 и описание ошибки –
«Ошибка разбора опций запроса» (листинг 1.128).
Листинг 1.128. Ответ сервера
{
″odata.error″: {
″code″: ″14″,
″message″: {
″lang″: ″ru″,
″value″: ″Error parsing $filter query option″
}
}
}

Если при указании адреса ресурса вы ошиблись, например, в имени параметра (листинг 1.129).
Листинг 1.129. URL запроса
localhost/REST/odata/standard.odata/Catalog_Сотрудники?$format=json&$select=Description

, ДатаРождения, Должность&$expan=Должность

В ответе сервера будет возвращен код ошибки – 0 и описание ошибки –
«Параметр не поддерживается» (листинг 1.130).

124

Технологии интеграции «1С:Предприятия 8.3»

Листинг 1.130. Ответ сервера
{
″odata.error″: {
″code″: ″0″,
″message″: {
″lang″: ″ru″,
″value″: ″The $expan parameter is not supported″
}
}
}

Если при указании адреса ресурса вы ошиблись, например, в имени функции
(листинг 1.131).
Листинг 1.131. URL запроса
localhost/REST/odata/standard.odata/InformationRegister_ЦеныТоваров/Slice_Last()

В ответе сервера будет возвращен код ошибки – 6 и описание ошибки –
«Метод не найден» (листинг 1.132).
Листинг 1.132. Ответ сервера
{
″odata.error″: {
″code″: ″6″,
″message″: {
″lang″: ″ru″,
″value″: ″Метод не найден″
}
}
}

ПОДРОБНЕЕ
Типичные ошибки, возникающие при получении данных через RESTинтерфейс, более подробно описаны в документации «1С:Предприятия»
в разделе «Глава 17. Механизмы интернет-сервисов – HTTP-сервисы –
Стандартный интерфейс ODATA – Ошибочные ситуации».

Глава 1. Интернет-технологии

125

Web-сервисы
Механизм Web-сервисов позволяет использовать систему «1С:Предприятие»
как набор сервисов в сложных распределенных и гетерогенных системах,
а также позволяет интегрировать ее с другими информационными системами
с использованием сервис-ориентированной архитектуры (SOA).
Сервис-ориентированная архитектура предлагает новый подход к созданию
распределенных информационных систем, в которых программные ресурсы
рассматриваются как сервисы, предоставляемые по сети.

Общая информация
Прикладное решение «1С:Предприятия» может являться как поставщиком
веб-сервисов, так и потребителем веб-сервисов, опубликованных другими
поставщиками (рис. 1.31).

Рис. 1.31. Поставщики и потребители веб-сервисов

Если прикладное решение является поставщиком веб-сервиса, то через
Web-сервисы оно может экспортировать свою функциональность вовне.
Web-сервисы определяются в дереве объектов конфигурации и становятся
доступны произвольным информационным системам после их публикации
на веб-сервере.
В этом случае и в файловом, и в клиент-серверном варианте работы взаимодействие между прикладным решением и потребителями веб-сервиса
осуществляется через веб-сервер с помощью модуля расширения веб-сервера
(рис. 1.32).

126

Технологии интеграции «1С:Предприятия 8.3»

Рис. 1.32. «1С:Предприятие» – поставщик веб-сервиса

При этом, когда потребитель обращается к Web-сервису прикладного
решения, выполняется модуль Web-сервиса. Этот модуль содержится
в конфигурации, и в нем располагаются процедуры, выполняемые при вызове
тех или иных операций Web-сервиса.
В случае клиент-серверного варианта работы этот модуль будет исполняться
в кластере. В случае файлового варианта работы – в модуле расширения
веб-сервера.
Если прикладное решение является потребителем веб-сервиса стороннего
поставщика, то оно может обращаться к Web-сервисам сторонних производителей как через статические ссылки на Web-сервисы, определенные
в дереве объектов конфигурации, так и с помощью динамических ссылок
на Web-сервисы, создаваемых с помощью встроенного языка.

Глава 1. Интернет-технологии

127

В этом случае взаимодействие между прикладным решением и поставщиком
веб-сервиса осуществляет клиентское приложение. Оно вызывает те или
иные операции веб-сервиса и обрабатывает полученные данные (рис. 1.33).

Рис. 1.33. «1С:Предприятие» – потребитель веб-сервиса

Для получения доступа к Web-сервису необходимо использовать
адрес, который формируется следующим образом: http://host/
base/ws/
или
http://host/base/ws/
. Рассмотрим составные части адреса:
■■ http://host/base – обычный URL, по которому выполняется доступ,
например, к информационной базе с помощью веб-клиента. Необходимо помнить, что при использовании информационной базы, в которой
настроено разделение данных, значения разделителей можно указывать
только в URL информационной базы. Указание разделителей с помощью
параметра Z не поддерживается;

128

Технологии интеграции «1С:Предприятия 8.3»

■■ ws – признак того, что выполняется обращение к Web-сервису;
■■ – имя Web-сервиса. Задается в свойстве объекта
Web-сервис;
■■ – описывает альтернативное имя для доступа
к Web-сервису. Задается в свойстве Имя файла публикации объекта
Web-сервис. Может быть изменено при публикации Web-сервиса.
Обращения по имени и адресу Web-сервиса являются равносильными.
ПОДРОБНЕЕ
Более подробно Web-сервисы описаны в документации «1С:Предприятия»
в разделе «Глава 17. Механизмы интернет-сервисов – Web-сервисы».

Предоставление функциональности через Web-сервисы
Чтобы функциональность системы «1С:Предприятие» стала доступной
внешним потребителям Web-сервисов, нужно создать в конфигурации необходимые Web-сервисы и затем опубликовать эти Web-сервисы на веб-сервере
с помощью специального инструмента конфигуратора (Администрирование >
Публикация на веб-сервере…). Процесс публикации подробно описан в книге
«1С:Предприятие 8.3. Руководство администратора», в главе 8 «Настройка
веб-серверов для работы с "1С:Предприятием"».
Разработка Web-сервиса
Для создания Web-сервиса нужно:
■■ Добавить в дерево конфигурации объект WebСервис и описать основные
свойства этого объекта:
□□ URI пространства имен. Каждый Web-сервис может быть однозначно идентифицирован по своему имени и URI пространства имен,
которому он принадлежит;
□□ Пакеты XDTO. Содержит перечень пакетов XDTO, типы которых
могут использоваться для описания типов параметров и возвращаемых значений Web-сервиса (см. раздел «Использование
XDTO для описания типов параметров и возвращаемых значений
Web-сервисов»);
□□ Имя
файла
публикации. Содержит имя файла описания
Web-сервиса, который опубликован на веб-сервере.

Глава 1. Интернет-технологии

129

■■ Описать операции, которые может выполнять данный Web-сервис.
Основные свойства операций:
□□ Тип возвращаемого значения. Содержит тип значения, которое
возвращает операция Web-сервиса. Может являться типом значения
XDTO или типом объекта XDTO (см. раздел «Использование
XDTO для описания типов параметров и возвращаемых значений
Web-сервисов»);
□□ Возможно пустое значение. Показывает, может ли значение,
возвращаемое операцией, принимать неопределенное значение;
□□ Имя метода. Содержит имя процедуры, расположенной в модуле
Web-сервиса, которая будет выполняться при вызове данного свойства.
■■ Описать параметры выполняемых операций. Основные свойства параметров:
□□ Тип
значения. Содержит тип значения параметра операции
Web-сервиса. Может являться типом значения XDTO или типом
объекта XDTO (см. раздел «Использование XDTO для описания типов
параметров и возвращаемых значений Web-сервисов»);
□□ Возможно пустое значение. Показывает, может ли параметр
операции принимать неопределенное значение;
□□ Направление
передачи. Определяет направление передачи
данных с помощью параметра. Входной параметр используется для
передачи данных Web-сервису, Выходной – для получения данных
от Web-сервиса, Входной-выходной может использоваться как для
передачи данных, так и для их получения от Web-сервиса.
■■ Создать модуль Web-сервиса и разработать в нем операции, определяющие его функциональность.
Объект конфигурации WebСервис содержит модуль, в котором создаются
процедуры на встроенном языке, выполняемые при вызове тех или иных
операций Web-сервиса. Типы параметров и возвращаемых значений этих
операций описываются с помощью типов XDTO и могут представлять собой
либо значения XDTO, либо объекты XDTO.
Механизм XDTO будет подробно рассмотрен в разделе «XDTOсериализация», но сейчас мы расскажем, как типы данных XDTO
используются в технологии Web-сервисов «1С:Предприятия».

130

Технологии интеграции «1С:Предприятия 8.3»

Использование XDTO для описания типов параметров
и возвращаемых значений Web-сервисов
Механизм XDTO позволяет определять объекты переноса данных, которые
могут образовывать строгую иерархию и сериализоваться в/из XML.
Эти свойства позволили использовать объекты XDTO в качестве параметров
и возвращаемых значений операций Web-сервисов.
Основным понятием, на котором строится механизм XDTO, является
фабрика XDTO. Фабрика XDTO содержит описание всех типов, с которыми
оперирует система. В частности, при создании новой информационной базы
«1С:Предприятия» автоматически создается глобальная фабрика XDTO,
которая описывает все типы, используемые в конфигурации. Эта фабрика
доступна через свойство глобального контекста ФабрикаXDTO.

Все типы данных XDTO подразделяются на типы-значения и типы-объекты.
Типы-значения позволяют определять простые типы, например: строки,
числа, даты, булевы значения и т. д. Типы-объекты позволяют определять
сложные типы, такие как структуры и массивы.
Инфраструктура XDTO определяет набор предопределенных типов
значений XDTO. Имена типов (string, int, date и т. п.) совпадают
с именами типов XML Schema и принадлежат URI пространства имен –
http://www.w3.org/2001/XMLSchema. Предопределенные типы являются автоматически зарегистрированными в любой фабрике XDTO.
Перед тем как использовать в Web-сервисе собственную структуру данных,
необходимо создать пакет XDTO, описывающий тип-объект структуры.
Для этого используются средства визуального конструирования, позволяющие добавлять пакеты XDTO в ветку дерева объектовконфигурации
Общие > XDTO-пакеты.
Пакет XDTO содержит описание некоторого множества типов, принадлежащих одному пространству имен – пространству имен пакета.
Структуры моделируются типами-объектами. Тип-объект может содержать
свойства, которые соответствуют элементам структуры. Каждое свойство
характеризуется уникальным именем и типом. Тип свойства может быть как
типом-значением, так и типом-объектом.
Новый объект XDTO может быть создан с помощью метода Создать()
фабрики XDTO на основе типа объекта XDTO. После этого следует
присвоить соответствующие значения свойствам объекта XDTO. Ниже
приведен пример создания объекта XDTO с именем Номенклатура и заполнения его свойств: Наименование, ПолноеНаименование, ЗакупочнаяЦена
и ШтрихКод (листинг 1.133).

Глава 1. Интернет-технологии

131

Листинг 1.133. Пример создания объекта структурного типа
// Создать ″пустой″ объект XDTO.
СтруктурныйТип = ФабрикаXDTO.Тип(″http://www.1c.ru/demos/products″, ″Номенклатура″);
Номенклатура = ФабрикаXDTO.Создать(СтруктурныйТип);
// Заполнить значения свойств объекта XDTO.
ОбъектСправочника = СсылкаНаЭлементСправочника.ПолучитьОбъект();
Номенклатура.Наименование = ОбъектСправочника.Наименование;
Номенклатура.ПолноеНаименование = ОбъектСправочника.ПолноеНаименование;
Номенклатура.ЗакупочнаяЦена = ОбъектСправочника.ЗакупочнаяЦена;
Номенклатура.ШтрихКод = ОбъектСправочника.ШтрихКод;

Массивы моделируются свойствами типов-объектов. Тип массива нельзя
создать напрямую, но для определенного свойства типа-объекта можно
указать минимальное и максимальное количество элементов массива.
Если оба значения равны 1, то это единичное свойство; если максимальное
количество больше 1, то множественное свойство; если же максимальное
количество равно -1, то количество элементов массива не ограничено.
Например, для создания массива элементов номенклатуры, определенного
в свойстве Элементы структуры НоменклатураГруппа, нужно выполнить следующий фрагмент кода (листинг 1.134).
Листинг 1.134. Пример создания массива объектов структурного типа
СтруктурныйТип = ФабрикаXDTO.Тип(″http://www.1c.ru/demos/products″, ″НоменклатураГруппа″);
НоменклатураГруппа = ФабрикаXDTO.Создать(СтруктурныйТип);
НоменклатураГруппа.Элементы.Добавить(Номенклатура);

Работа с Web-сервисами сторонних поставщиков
Система «1С:Предприятие» может использовать Web-сервисы, предоставляемые другими поставщиками, следующими способами:
■■ с помощью статических ссылок, создаваемых в дереве объектов конфигурации;
■■ с помощью динамических ссылок, создаваемых средствами встроенного
языка;
■■ комбинацией предыдущих способов.
При использовании статической ссылки система «1С:Предприятие» получает
описание Web-сервиса поставщика только один раз, при создании ссылки.
За счет этого достигается большая скорость работы.

132

Технологии интеграции «1С:Предприятия 8.3»

При использовании динамической ссылки описание Web-сервиса получается
каждый раз при вызове Web-сервиса. Скорость работы при этом уменьшается, но зато такой подход обеспечивает актуальность описания Web-сервиса
поставщика. В случае же использования статических ссылок для получения
актуального описания Web-сервиса требуется выполнить повторный импорт
WSDL-описания средствами конфигуратора и затем сохранить измененную
конфигурацию.
Например, для Web-сервиса с URI пространства имен http://www.
MyCompany.ru/shipment, именем ДанныеРасходнойНакладной и именем
файла публикации Shipment.1cws статическая ссылка создается путем
импорта WSDL-описания опубликованного сервиса на основе URL –
http://www.MyCompany.ru/shipment/ws/Shipment.1cws?wsdl.
А создание WS-прокси на основании WS-ссылки будет выглядеть следующим образом (листинг 1.135).
Листинг 1.135. Использование статической WS-ссылки
// Создать WS-прокси на основании WS-ссылки.
Прокси = WSСсылки.ДанныеРасходнойНакладной.СоздатьWSПрокси(
″http://www.MyCompany.ru/shipment″, ″ДанныеРасходнойНакладной″, ″ДанныеРасходнойНакладнойSoap″);

При использовании динамической ссылки на этот Web-сервис WS-прокси
создается на основании WS-определения (листинг 1.136).
Листинг 1.136. Использование динамической WS-ссылки
// Создать WS-прокси на основании WS-определения.
Определение = Новый WSОпределения(″http://www.MyCompany.ru/shipment/ws/Shipment.1cws?wsdl″);
Прокси = Новый WSПрокси(Определение, ″http://www.MyCompany.ru/shipment″

, ″ДанныеРасходнойНакладной″, ″ДанныеРасходнойНакладнойSoap″);

На практике бывают ситуации, когда один и тот же Web-сервис предоставляется по разным адресам (URL), однако имеет абсолютно одинаковое
описание (WSDL). Например, когда используется тиражируемый сервис,
выполняющий некоторую функцию. При этом адрес сервиса может быть
различным. В этом случае можно использовать комбинированный способ.
То есть сначала загрузить в конфигурацию описание Web-сервиса (добавить
в дерево объектов конфигурации статическую ссылку на Web-сервис), а затем
во время использования указать конкретный адрес, по которому расположен
Web-сервис.
Например, если адрес реального расположения сервиса отличается от адреса,
который использовался во время загрузки описания Web-сервиса в конфигурацию, то можно предусмотреть ввод адреса конкретного экземпляра сервиса
в настройках прикладного решения, и затем этот новый адрес явно указать
при создании объекта WSПрокси (листинг 1.137).

Глава 1. Интернет-технологии

133

Листинг 1.137. Комбинированный способ на основе статической ссылки
// Создать WS прокси на основании ссылки.
Прокси = WSСсылки.ДанныеРасходнойНакладной.СоздатьWSПрокси(″http://www.MyCompany.ru/shipment″,
″ДанныеРасходнойНакладной″, ″ДанныеРасходнойНакладнойSoap″, , , , ″http://www.realURL/realPath″);

Или же можно использовать динамическую ссылку, но адрес расположения
Web-сервиса получать не из файла описания (WSDL), а непосредственно
указывать при создании объекта (листинг 1.138).
Листинг 1.138. Комбинированный способ на основе динамической ссылки
// Создать WS-прокси на основании WS-определения.
Определение = Новый WSОпределения(″http://www.MyCompany.ru/shipment/ws/Shipment.1cws?wsdl″);
Прокси = Новый WSПрокси(Определение, ″http://www.MyCompany.ru/shipment″, ″ДанныеРасходнойНакладной″,

″ДанныеРасходнойНакладнойSoap″, , , , ″http://www.realURL/realPath″);

При попытке загрузить описание Web-сервиса в конфигураторе (создание
статической ссылки) или при использовании динамической ссылки (при
помощи объекта WSОпределения) система выполняет проверку загружаемого
описания Web-сервиса (WSDL). Если в описании Web-сервиса присутствует
ошибка (с точки зрения системы «1С:Предприятие»), то описание не будет
загружено и будет сгенерировано исключение. В тексте исключения будет
находиться подробная диагностика причин отказа в загрузке.
ПОДРОБНЕЕ
Ошибки, которые могут возникнуть при проверке описания Web-сервиса,
более подробно описаны в документации «1С:Предприятия» в разделе
«Глава 17. Механизмы интернет-сервисов – Web-сервисы – Работа с вебсервисами сторонних поставщиков – Общая информация».

Пример реализации Web-сервиса
В этом разделе мы разработаем Web-сервис, который будет получать незакрытые заказы товаров и возвращать пользователю сервиса всю информацию
о таких заказах.
Во время разработки мы покажем процесс создания Web-сервиса, определения его основных свойств и работу с пакетами передачи данных XDTO.
Для реализации этой функциональности мы добавим для Web-сервиса соответствующую операцию и создадим функцию-обработчик этой операции
в модуле Web-сервиса.

134

Технологии интеграции «1С:Предприятия 8.3»

Затем мы покажем процесс использования функциональности, предоставляемой этим Web-сервисом. Для простоты реализуем это в демонстрационной
обработке, в той же самой конфигурации, где и создавался Web-сервис, хотя,
конечно же, в действительности это будет происходить извне прикладного
решения.
Для доступа к Web-сервису через WS-прокси будет использоваться
статическая ссылка. С помощью WS-прокси мы выполним операцию, реализованную для Web-сервиса. После этого мы покажем возможную обработку
данных, полученных от Web-сервиса. А именно – создадим новые расходные
накладные на основании полученных заказов.
ПОДРОБНЕЕ
Подробнее познакомиться с реализацией и использованием Web-сервиса
можно в демонстрационной конфигурации «Примеры работы», которая
прилагается к книге.

Закрытие заказов на товары
В данном примере мы рассмотрим ситуацию, когда в одном прикладном
решении существуют заказы товаров, а в другом прикладном решении на
основании этих заказов, не дожидаясь сеанса обмена данными, должны быть
созданы расходные накладные. При этом нормативно-справочная информация у этих решений общая.
Для этого в прикладном решении поставщика Web-сервиса нам нужно разработать Web-сервис, который будет получать незакрытые заказы товаров
и возвращать всю информацию о таких заказах. И затем опубликовать этот
сервис на веб-сервере. А в прикладном решении потребителя Web-сервиса
нам нужно реализовать функциональность (например, команду), при выполнении которой на основании полученных от Web-сервиса заказов будут
сформированы соответствующие документы продаж.
Итак, приступим.
Сначала для описания типов параметров и возвращаемых значений
Web-сервиса в ветке Общие > XDTO-пакеты добавим пакет XDTO ДанныеПередачи с пространством имен http://localhost/REST. В свойстве URI
пространства имен содержится http://localhost – адрес веб-сервера,
установленного на локальном компьютере, /REST – каталог, в который будет
опубликован Web-сервис.

Глава 1. Интернет-технологии

135

Пакет XDTO будет содержать следующие типы объектов XDTO (рис. 1.34):

Рис. 1.34. Структура пакета XDTO «ДанныеПередачи»

■■ Товар – для передачи данных элемента справочника Товары. Этот тип
объектов XDTO будет содержать свойство:
□□ Наименование – тип string из пространства имен http://www.
w3.org/2001/XMLSchema.
■■ Клиент – для передачи данных элемента справочника Клиенты. Этот тип
объектов XDTO будет содержать свойство:
□□ Наименование – тип string из пространства имен http://www.
w3.org/2001/XMLSchema.
■■ СтрокаЗаказа – для передачи данных одной строки состава заказа товаров.
Этот тип объектов XDTO будет содержать следующие свойства:
□□ Товар – тип Товар из пространства имен http://localhost/REST.
Представляет собой ссылку на объект XDTO, определенный нами
ранее, в предыдущем примере;
□□ Количество – тип int из пространства имен http://www.
w3.org/2001/XMLSchema;
□□ Цена – тип int из пространства имен http://www.w3.org/2001/
XMLSchema;
□□ Сумма – тип int из пространства имен http://www.w3.org/2001/
XMLSchema.

136

Технологии интеграции «1С:Предприятия 8.3»

■■ Заказ – для передачи всех данных одного заказа. Этот тип объектов XDTO
будет содержать следующие свойства:
□□ Дата – тип date из пространства имен http://www.w3.org/2001/
XMLSchema;
□□ Клиент – тип Клиент из пространства имен http://localhost/
REST. Представляет собой ссылку на объект XDTO, определенный
нами выше;
□□ Состав – тип СтрокаЗаказа
из пространства имен http://
localhost/REST. Представляет собой ссылку на объект XDTO,
определенный нами выше. Для того чтобы это свойство могло содержать неограниченное множество значений, установим его свойство
Максимальное количество в значение -1 (рис. 1.35);

Рис. 1.35. Свойства свойства «Состав» типа объекта XDTO «Заказ»

□□ Представление – тип string из пространства имен http://www.
w3.org/2001/XMLSchema.
■■ Заказы – для передачи данных всех заказов. Этот тип объектов XDTO
будет содержать единственное свойство:
□□ Список – тип Заказ из пространства имен http://localhost/
REST. Представляет собой ссылку на объект XDTO, определенный
нами выше. Для того чтобы это свойство могло содержать неограниченное множество значений, установим его свойство Максимальное
количество в значение -1.
Теперь, когда необходимые типы объектов XDTO созданы, в ветке
Общие > Web-сервисы добавим Web-сервис с именем Заказы со следующими
свойствами (рис. 1.36):

Глава 1. Интернет-технологии

137

Рис. 1.36. Свойства Web-сервиса «Заказы»

■■ URI пространства имен − http://localhost/REST;
■■ Пакеты XDTO − http://localhost/REST;
■■ Имя файла публикации – wsorders.1cws.
На закладке Операции в окне свойств Web-сервиса определим операцию
ПолучитьНезакрытыеЗаказы со следующими свойствами (рис. 1.37):

Рис. 1.37. Свойства операции «ПолучитьНезакрытыеЗаказы»

■■ Тип возвращаемого значения – Заказы из
http://localhost/REST;
■■ Возможно пустое значение – установлен;
■■ Имя процедуры – ПолучитьНезакрытыеЗаказы.

пространства

имен

138

Технологии интеграции «1С:Предприятия 8.3»

Эта операция будет возвращать список всех незакрытых заказов на товары,
т. е. тех заказов, у которых признак закрытия еще не установлен и, следовательно, на их основании еще не создавались расходные накладные.
Нажмем на кнопку открытия со значком лупы у свойства Имя процедуры.
В результате в модуле Web-сервиса будет создан шаблон функции-обработчика ПолучитьНезакрытыеЗаказы(), которая будет выполняться при его
вызове. Заполним эту функцию следующим образом (листинг 1.139).
Листинг 1.139. Функция «ПолучитьНезакрытыеЗаказы()»
Функция ПолучитьНезакрытыеЗаказы()
// Сформировать запрос к списку незакрытых заказов.
Запрос = Новый Запрос;
Запрос.Текст = ″ВЫБРАТЬ
| Заказ.Ссылка КАК Ссылка,
| Заказ.Дата КАК Дата,
| Заказ.Клиент КАК Клиент,
| Заказ.Товары.(
|
Товар КАК Товар,
|
Количество КАК Количество,
|
Цена КАК Цена,
|
Сумма КАК Сумма
| ) КАК Товары,
| Заказ.Представление КАК ПредставлениеЗаказа
|ИЗ
| Документ.Заказ КАК Заказ
|ГДЕ
| Заказ.Закрыт = ЛОЖЬ
| И Заказ.Проведен = ИСТИНА″;
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда

Возврат Неопределено;
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
// Получить типы объектов XDTO.
ТоварТип = ФабрикаXDTO.Тип(″http://localhost/REST″, ″Товар″);
КлиентТип = ФабрикаXDTO.Тип(″http://localhost/REST″, ″Клиент″);
СтрокаЗаказаТип = ФабрикаXDTO.Тип(″http://localhost/REST″, ″СтрокаЗаказа″);
ЗаказТип = ФабрикаXDTO.Тип(″http://localhost/REST″, ″Заказ″);
ЗаказыТип = ФабрикаXDTO.Тип(″http://localhost/REST″, ″Заказы″);
// Создать объект XDTO список заказов.
Заказы = ФабрикаXDTO.Создать(ЗаказыТип);
// В цикле обхода выборки получить список и содержимое незакрытых заказов.
Пока Выборка.Следующий() Цикл

// Создать объект XDTО Заказ и Клиент.

Заказ = ФабрикаXDTO.Создать(ЗаказТип);

Глава 1. Интернет-технологии

139


Клиент = ФабрикаXDTO.Создать(КлиентТип);

// Заполнить свойства клиента и заказа.

Клиент.Наименование = Выборка.Клиент.Наименование;

Заказ.Клиент = Клиент;

Заказ.Дата = Выборка.Дата;

Заказ.Представление = Выборка.ПредставлениеЗаказа;


ВыборкаСостав = Выборка.Товары.Выбрать();

// В цикле обхода состава выборки получить состав заказа

Пока ВыборкаСостав.Следующий() Цикл

// Создать объекты XDTO строки заказа и товара.

СтрокаЗаказа = ФабрикаXDTO.Создать(СтрокаЗаказаТип);

Товар = ФабрикаXDTO.Создать(ТоварТип);

// Заполнить свойства товара и строки заказа.

Товар.Наименование = ВыборкаСостав.Товар.Наименование;

СтрокаЗаказа.Товар = Товар;

СтрокаЗаказа.Цена = ВыборкаСостав.Цена;

СтрокаЗаказа.Количество = ВыборкаСостав.Количество;

СтрокаЗаказа.Сумма = ВыборкаСостав.Сумма;

// Добавить строку заказа.

Заказ.Состав.Добавить(СтрокаЗаказа);

КонецЦикла;


// Добавить заказ в список заказов.

Заказы.Список.Добавить(Заказ);


// Закрыть заказ.

Документ = Выборка.Ссылка.ПолучитьОбъект();

Документ.Закрыт = Истина;

Документ.Записать();
КонецЦикла;
Возврат Заказы;

КонецФункции

Прокомментируем код функции.
Сначала мы выполняем запрос, получающий информацию обо всех незакрытых заказах.
После этого с помощью глобальной фабрики XDTO получаем типы объектов
XDTO, ранее описанные нами в пакете XDTO ДанныеПередачи: ТоварТип,
КлиентТип, СтрокаЗаказаТип, ЗаказТип и ЗаказыТип. На основе
последнего типа мы создаем объект XDTO Заказы.

В цикле обхода выборки из результата запроса мы создаем объект XDTO
Клиент, заполняем его наименование и объект XDTO Заказ и заполняем
его свойства: Клиент, Представление и Дата.

Затем мы обходим табличную часть заказа Товары. В цикле обхода
выборки состава заказа мы создаем объект XDTO Товар, заполняем

140

Технологии интеграции «1С:Предприятия 8.3»

его наименование и объект XDTO СтрокаЗаказа и заполняем его
свойства: Товар, Количество, Цена и Сумма. После этого объект СтрокаЗаказа добавляем в список значений XDTO Заказ – Заказ.Состав.
Добавить(СтрокаЗаказа).

А при завершении цикла обхода родительской выборки объект Заказ
добавляем в список значений XDTO Заказы – Заказы.Список.
Добавить(Заказ).

После этого от ссылки на заказ получается объект, признак заказа Закрыт
устанавливается в Истина, заказ записывается. И, таким образом, заказ
закрывается – следовательно, при следующем вызове Web-сервиса на основании этого заказа уже не будет создаваться расходная накладная.
В заключение функция возвращает объект XDTO Заказы. Тип этого объекта
был описан в свойстве Тип возвращаемого значения операции ПолучитьНезакрытыеЗаказы созданного нами Web-сервиса Заказы.
Теперь осталось только опубликовать созданный Web-сервис на веб-сервере
– например, расположенном на локальном компьютере http://localhost
в каталоге /REST. Для этого в диалоге публикации информационной базы
на веб-сервере на закладке Web-сервисы установим флажок Публиковать
Web-сервисы по умолчанию и флажок Публиковать Web-сервисы и отметим
имеющиеся Web-сервисы, к которым мы хотим предоставить доступ из
внешних систем (рис. 1.38).

Рис. 1.38. Диалог публикации на веб-сервере

Глава 1. Интернет-технологии

141

Теперь посмотрим, как используется функциональность, предоставляемая
Web-сервисом Заказы, в прикладном решении «1С:Предприятия».
Например, при нажатии кнопки Закрыть заказы в форме демонстрационной
обработки вызывается серверная процедура ЗакрытьЗаказыНаСервере()
(листинг 1.140).
Листинг 1.140. Функция «ЗакрытьЗаказыНаСервере()»
&НаСервереБезКонтекста
Процедура ЗакрытьЗаказыНаСервере()
// Создать WS-прокси на основании WS-ссылки.
Прокси = WSСсылки.ДанныеЗаказов.СоздатьWSПрокси(″http://localhost/REST″, ″Заказы″, ″ЗаказыSoap″);
// Выполнить операцию Веб-сервиса Заказы.
СписокЗаказов = Прокси.ПолучитьНезакрытыеЗаказы();
Если СписокЗаказов = Неопределено Тогда

Возврат;
КонецЕсли;
Для Каждого Заказ Из СписокЗаказов.Список Цикл

// Создать расходную накладную на основании заказа.

ДокументОбъект = Документы.РасходнаяНакладная.СоздатьДокумент();

ДокументОбъект.Дата = Заказ.Дата;

// Найти клиента по наименованию.

ДокументОбъект.Клиент = Справочники.Клиенты.НайтиПоНаименованию(

Заказ.Клиент.Наименование);

ДокументОбъект.Основание = Заказ.Представление;


// Заполнить ТЧ расходной накладной данными заказа.

Для Каждого СтрокаСостава Из Заказ.Состав Цикл

НоваяСтрока = ДокументОбъект.Товары.Добавить();

НоваяСтрока.Количество = СтрокаСостава.Количество;

НоваяСтрока.Цена = СтрокаСостава.Цена;

НоваяСтрока.Сумма = СтрокаСостава.Сумма;


// Найти товар по наименованию.

НоваяСтрока.Товар = Справочники.Товары.НайтиПоНаименованию(

СтрокаСостава.Товар.Наименование);

КонецЦикла;


ДокументОбъект.Записать(, РежимПроведенияДокумента.Неоперативный);
КонецЦикла;
КонецПроцедуры

В данной процедуре используется статическая ссылка на Web-сервис.
Для этого в дерево объектов конфигурации в ветку Общие > WS-ссылки мы
добавили объект WSСсылка с именем ДанныеЗаказов, ссылающийся на
опубликованный Web-сервис. При создании ссылки мы выполнили импорт
WSDL-описания Web-сервиса, указав в качестве URL http://localhost/
REST/ws/wsorders.1cws?wsdl (рис. 1.39).

142

Технологии интеграции «1С:Предприятия 8.3»

Рис. 1.39. Создание статической WS-ссылки

На основе статической ссылки на Web-сервис создается объект WSПрокси,
с помощью которого выполняется операция Web-сервиса ПолучитьНезакрытыеЗаказы().

В результате список незакрытых заказов возвращается Web-сервисом в виде
списка XDTO в переменную СписокЗаказов.
Далее в цикле обхода этого списка (СписокЗаказов.Список) на основании
каждого заказа создается расходная накладная, и ее реквизиты: Дата, Клиент,
Основание, а также табличная часть Товары заполняются данными заказа
(рис. 1.40, 1.41).

Рис. 1.40. Заказы товаров и соответствующие им расходные накладные

Глава 1. Интернет-технологии

143

Рис. 1.41. Заказы товаров и соответствующие им расходные накладные

Таким образом, в прикладном решении потребителя Web-сервиса появятся
расходные накладные, соответствующие заказам товаров, которые
существуют у поставщика этого Web-сервиса.

144

Технологии интеграции «1С:Предприятия 8.3»

Web-сервисы в расширениях
В расширениях также можно создавать собственные Web-сервисы. Таким
образом в процессе внедрения, не изменяя конфигурацию, находящуюся
на поддержке, можно настроить взаимодействие прикладного решения
с внешними системами. Например, для синхронизации данных между
прикладным решением и веб-сайтом.
Для этого в расширении в ветку Общие > Web-сервисы нужно добавить
Web-сервис, разработать его по аналогии с описанными выше примерами
и опубликовать на веб-сервере, установив в диалоге публикации флажок
Публиковать Web-сервисы расширений по умолчанию.
Как правило, Web-сервисы не существуют сами по себе, а ссылаются
на XDTO-пакеты. Поэтому в расширении можно создать не только
собственные Web-сервисы, но и собственные XDTO-пакеты. А также XDTOпакеты (или их отдельные элементы) можно заимствовать из основной
конфигурации.
Кроме того, в расширении можно создавать и собственные WS-ссылки,
с помощью которых статически описывать сторонние веб-сервисы, к которым
обращается конфигурация.
В отличие от Web-сервисов, созданных в основной конфигурации, установка в диалоге публикации флажка Публиковать Web-сервисы расширений
по умолчанию не позволяет выбирать для публикации конкретные сервисы
расширений. Поэтому, если требуется опубликовать лишь некоторые из них,
можно вручную отредактировать файл публикации (default.vrd) и удалить там
«ненужные» сервисы.

Повторное использование сеансов
интернет-сервисов
В данном разделе мы рассмотрим, как можно повысить производительность
веб-сервисов при помощи повторного использования сеансов этих сервисов.
В данном случае под веб-сервисами понимаются любые интернет-сервисы:
Web-сервис, HTTP-сервис, а также стандартный интерфейс Odata.
Дело в том, что если не переиспользовать сеансы веб-сервисов, то при
каждом вызове сервиса система будет создавать новый сеанс работы с информационной базой. При этом выполняется довольно «тяжелый» обработчик
УстановкаПараметровСеанса() и др. При завершении вызова интернетсервиса сеанс завершается, а при повторном вызове система опять тратит
время и ресурсы на создание сеанса.

Глава 1. Интернет-технологии

145

Кроме этого существует и функциональный недостаток. Веб-сервисы
не обладают состоянием. Однако иногда требуется реализовывать логику,
использующую сохранение состояния между вызовами веб-сервиса.
Для устранения этих недостатков в настоящее время в платформе реализованы две различные стратегии, обеспечивающие переиспользование сеансов:
■■ автоматическое переиспользование сеансов из пула;
■■ управление сеансами с помощью HTTP-заголовков.
Необходимость использования той или иной стратегии можно определить
в свойствах объектов конфигурации Web-сервис и HTTP-сервис, а также, при
необходимости, переопределить в файле публикации default.vrd.
Свойство объектов конфигурации Web-сервис и HTTP-сервис Повторное
использование сеансов может принимать значения:

■■ Использовать. В этом случае временем жизни и характером повторного использования сеанса управляет непосредственно клиент
интернет-сервиса. Для управления созданием нового сеанса используются
специальные заголовки HTTP-запроса к интернет-сервису.
■■ Использовать автоматичесЧки (значение по умолчанию). В этом случае
системой организуется пул сеансов. Каждый сеанс в пуле характеризуется
некоторым набором параметров. Если сеанс в пуле свободен и характеризуется тем же набором параметров, что и запрос интернет-сервиса, то
запросу сразу будет предоставлен существующий сеанс.
■■ Не использовать. При каждом обращении к интернет-сервису будет создаваться новый сеанс.
Свойство Время жизни сеанса позволяет указать:

■■ через какой промежуток времени «свободный» сеанс будет удален из пула
сеансов в случае автоматического повторного использования сеансов;
■■ через какое время бездействия сеанс будет принудительно завершен
системой при ручном повторном использовании сеансов.
Если данное свойство установлено в значение 0, то это означает, что сеансы
повторно не используются. Такая установка эквивалентна установке свойства
Повторное использование сеансов в значение Не использовать.
В файле публикации на веб-сервере default.vrd для элементов, описывающих
SOAP-сервисы, HTTP-сервисы и сервисы OData, также существуют соответствующие атрибуты:
■■ reuseSessions – аналог свойства ПовторноеИспользованиеСеансов, может принимать значения autouse, use и dontuse;
■■ sessionMaxAge – аналог свойства ВремяЖизниСеанса;

146

Технологии интеграции «1С:Предприятия 8.3»

■■ poolTimeout – используется при автоматическом управлении сеансами,
содержит время ожидания доступности сеанса;
■■ poolSize – используется при автоматическом управлении сеансами,
содержит максимальное количество сеансов, которое может быть создано
в пуле.
Например, файл default.vrd может выглядеть следующим образом (листинг
1.141).
Листинг 1.141. Файл публикации на веб-сервере

















Глава 1. Интернет-технологии

147

Файл default.vrd имеет больший приоритет, чем конфигурация. При публикации конфигурации атрибуты reuseSessions и sessionMaxAge
заполняются из соответствующих свойств объектов конфигурации
Web-сервис и HTTP-сервис. Но при необходимости можно изменить
эти значения прямо в файле, и тогда платформа будет использовать их,
а не значения из конфигурации.

Автоматическое переиспользование сеансов
Стратегия, реализующая автоматическое переиспользование сеансов
из пула, подходит для высоконагруженных публичных сервисов, к которым
обращаются клиенты, выполняющие шаблонные операции и обладающие
унифицированными привилегиями.
Сеансы в пуле хранятся в разрезе типа сервиса, наименования сервиса,
пользователя/пароля, значений разделителей и безопасного режима. Причем
в пуле может быть несколько сеансов с одинаковыми значениями перечисленных реквизитов.
При обнаружении входящего запроса к интернет-сервису система анализирует текущий пул сеансов и ищет сеанс с параметрами, в точности
соответствующими входящему запросу. Если такой сеанс обнаруживается и
он не используется запросом от другого клиента интернет-сервиса, входящий
запрос будет обслуживаться найденным сеансом. Если свободного сеанса
не найдено – выполняется попытка создать новый сеанс. Если при попытке
создания нового сеанса будет превышен размер пула сеансов (poolSize),
входящий запрос будет ожидать некоторое время (poolTimeout). Если по
истечении таймаута пула свободный сеанс не обнаруживается – интернетзапрос будет завершен с ошибкой 406 «Not Acceptable». Свободный сеанс
будет уничтожен системой автоматически после истечения времени жизни
свободного сеанса (ВремяЖизниСеанса).
Настройки автоматического пула сеансов действуют в рамках публикации.
Таким образом, если для некоторой информационной базы созданы несколько
публикаций, то при вызове интернет-сервиса используется настройка пула
той публикации, через которую выполняется вызов.
При выборе размера пула следует учитывать все разрезы, в которых хранятся
сеансы в пуле. Рекомендуется подбирать размер пула сеансов с небольшим
запасом, чтобы при максимальной загрузке в пуле оставалось несколько
свободных сеансов. Иначе при пиковой нагрузке некоторые вызовы будут
завершаться с ошибками.

148

Технологии интеграции «1С:Предприятия 8.3»

Однако эта стратегия не подходит для сценариев, в которых нужно использовать сохраненное состояние сеанса на сервере. Потому что нет никакой
гарантии, что при следующем вызове клиент будет подключен к тому же
самому сеансу, так как в пуле может быть несколько сеансов с одинаковыми
значениями ключевых реквизитов.

Ручное управление сеансами
Стратегия ручного управления сеансами подразумевает, что клиент интернетсервиса самостоятельно управляет количеством сеансов и временем их
жизни. Эта стратегия лучше подходит для высокоинтегрированных систем
в рамках одной организации. Разработчик может реализовать собственный
алгоритм, который будет управлять временем жизни сеансов и их количеством.
В качестве управляющего элемента выступает заголовок HTTP-запроса
IBSession. Этот заголовок может принимать два значения:

■■ start – в этом случае система «1С:Предприятие» создает новый сеанс,
выполняет аутентификацию, устанавливает разделители, выполняются
все необходимые обработчики событий. Если система не может создать
новый сеанс, то клиент получит ошибку 406 «Not Acceptable». Если
создание сеанса выполнено успешно, в HTTP-ответ помещается директива установки cookie IBsession с идентификатором созданного сеанса:
Set-Cookie: IBsession=.
При необходимости использовать ранее созданный сеанс необходимо
в HTTP-запросе к системе «1С:Предприятие» указать идентификатор
ранее созданного сеанса в заголовке: Cookie:
IBsession=. Если в запросе указывается идентификатор сеанса, который
ранее не создавался или был завершен, клиент получает ошибку 400 «Bad
Request».
Если HTTP-запрос не содержит заголовка IBSession, то сеанс создается
и завершается при каждом вызове интернет-сервиса.
■■ finish – в этом случае система «1С:Предприятие» завершает сеанс,
который указан в запросе (Cookie: IBsession=), одновременно с командой завершения сеанса. Завершение сеанса произойдет
автоматически, если в этом сеансе не выполнялось никаких действий
за время жизни сеанса.
Пул сеансов в этом случае не используется.
Эта стратегия позволяет реализовать сценарии, в которых используется
состояние сеанса, сохраненное на сервере. Потому что в ответе сервера
содержится уникальный идентификатор созданного сеанса. Однако нужно

Глава 1. Интернет-технологии

149

помнить, что завершение сеанса может происходить автоматически, без
участия разработчика, когда превышен период бездействия сеанса, поэтому
сеансовые данные могут быть сброшены.

Коды состояния в ответах HTTP-сервера
В данном разделе будут рассмотрены различные коды ответов, возвращаемые HTTP-сервером, а точнее – коды состояния, содержащиеся в свойстве
КодСостояния объектов HTTPСервисОтвет и HTTPОтвет.
ПОДРОБНЕЕ
Более подробно коды состояний, возвращаемые сервером, описаны
в протоколе по адресу http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.

Если при выполнении HTTP-запроса на веб-сервере возникает фатальная
ошибка, то в большинстве случаев нужно остановить работу и показать
пользователю сообщение об ошибке, включив в него код состояния, возвращенный HTTP-сервером.
Прежде всего, если при попытке выполнить HTTP-запрос на веб-сервере
было вызвано исключение, то это говорит о том, что запрос не дошел
до HTTP-сервера.
Коды состояний 4XX сигнализируют о неправильном запросе. Причины
ошибок могут быть следующими:
■■ сервер не может понять запрос из-за неправильного синтаксиса;
■■ сервер не нашел ничего, соответствующего Request-URI;
■■ метод, указанный в строке запроса, не разрешен для ресурса, идентифицируемого Request-URI;
■■ запрос требует аутентификации пользователя и др.
Коды состояний 5XX сигнализируют о проблемах на сервере (возможно,
на прокси-сервере). Причины ошибок могут быть следующими:
■■ сервер не поддерживает функциональность, необходимую для выполнения запроса;
■■ сервер не может обработать запрос из-за временной перегрузки или
обслуживания сервера;
■■ на сервере произошла внутренняя ошибка, которая не позволила ему
выполнить запрос;
■■ сервер не поддерживает или отказывается поддерживать версию протокола HTTP, которая использовалась в сообщении запроса и др.

150

Технологии интеграции «1С:Предприятия 8.3»

Коды состояний 3XX означают, что клиент должен предпринять дальнейшие
действия для выполнения запроса. При этом запрос должен быть перенаправлен на новый адрес. Причины ошибок могут быть следующими:
■■ запрошенному ресурсу был назначен новый постоянный URI;
■■ запрашиваемый ресурс временно находится под другим URI и др.
Коды состояний 1XX и 2XX означают, что запрос клиента был успешно
получен, понят и принят.
Ниже приводится пример процедуры ВыполнитьHTTPЗапрос(), которая
получает в качестве параметра адрес ресурса. В процедуре устанавливается
HTTP-соединение с локальным веб-сервером «localhost», затем на основе
адреса ресурса создается HTTP-запрос и выполняется на сервере методом
Получить() объекта HTTPСоединение. Ответ сервера в виде объекта
HTTPОтвет возвращается в переменную Результат (листинг 1.142).
Листинг 1.142. Процедура «ВыполнитьHTTPЗапрос»
&НаСервереБезКонтекста
Процедура ВыполнитьHTTPЗапрос(АдресРесурса)
СерверИсточник = ″localhost″;
Сообщение = Новый СообщениеПользователю;
// Создать HTTP-соединение с сервером localhost.
HTTPСоединение = Новый HTTPСоединение(СерверИсточник);
// Создать HTTP-запрос на основе URL.
HTTPЗапрос = Новый HTTPЗапрос(АдресРесурса);
Попытка

Результат = HTTPСоединение.Получить(HTTPЗапрос);
Исключение

Сообщение.Текст = ″Произошла сетевая ошибка!″;

Сообщение.Сообщить();

ВызватьИсключение;
КонецПопытки;
Если Результат.КодСостояния >= 400 и Результат.КодСостояния < 500 Тогда

Сообщение.Текст = ″Код статуса больше 4XX, ошибка запроса. Код статуса:

″ + Результат.КодСостояния;
КонецЕсли;
Если Результат.КодСостояния >= 500 и Результат.КодСостояния < 600 Тогда

Сообщение.Текст = ″Код статуса больше 5XX, ошибка сервера. Код статуса:

″ + Результат.КодСостояния;
КонецЕсли;
// Обработать перенаправление.
Если Результат.КодСостояния >= 300 и Результат.КодСостояния < 400 Тогда

Сообщение.Текст = ″Код статуса больше 3XX, Перенаправление. Код статуса:

″ + Результат.КодСостояния;

Глава 1. Интернет-технологии

151



Если Результат.КодСостояния = 302 Тогда

Сообщение.Текст = Сообщение.Текст + Символы.ПС

+ ″Код статуса 302, Постоянное перенаправление.″;

АдресРесурса = Результат.Заголовки.Получить(″Location″);


Если АдресРесурса Неопределено Тогда

Сообщение.Текст = Сообщение.Текст + Символы.ПС

+ ″Выполняю запрос по новому адресу ″ +

Символы.ПС + АдресРесурса;

ВыполнитьHTTPЗапрос(АдресРесурса);

Иначе

Сообщение.Текст = Сообщение.Текст + Символы.ПС

+ ″Сервер не сообщил адрес ресурса!″;

КонецЕсли;

КонецЕсли;
КонецЕсли;
Если Результат.КодСостояния < 300 Тогда

Сообщение.Текст = ″Скорее всего все хорошо!″;

Сообщение.Текст = Сообщение.Текст + Символы.ПС + ″Код статуса: ″ + Результат.КодСостояния;
КонецЕсли;
Сообщение.Сообщить();
КонецПроцедуры

В этой процедуре обрабатываются и выводятся пользователю различные
коды состояния HTTP-сервера. Отдельно анализируются коды ответа,
которые сигнализируют о перенаправлении запросов. Если код состояния –
302, то процедура выполняется заново, с новым адресом ресурса.

FTP-соединение
В этом разделе мы рассмотрим, как организовать взаимодействие
с FTP-сервером по FTP-протоколу обмена файлами (FTPS, FTPES).
Для указания параметров доступа к FTP-серверу используется объект
FTPСоединение.
При этом разработчику доступны следующие возможности:

■■ определить активный или пассивный режим работы FTP-соединения;
■■ установить пользователя, от имени которого установлено соединение,
порт сервера, с которым произведено соединение, сервер (прокси), через
который установлено соединение;
■■ записать (найти) файлы объектов типа FTPФайл;
■■ переименовать файлы и каталоги на сервере;

152

Технологии интеграции «1С:Предприятия 8.3»

■■
■■
■■
■■
■■
■■

получить ресурс из указанного адреса;
создать (удалить) каталог на сервере;
установить текущий каталог на сервере;
определить размер файла (в байтах);
определить параметры файлов и каталогов;
использовать протокол TLS 1.0 – TLS1.2 для установления защищенного
соединения;
■■ проверить сертификат сервера с использованием стандартных механизмов
ОС или файла сертификата.
Ниже мы рассмотрим небольшие примеры, как с помощью методов Получить() и Записать() объекта FTPСоединение получать и записывать
файлы с/на FTP-сервер. А также – как получать файлы с сервера с помощью
прямого копирования методом глобального контекста НачатьКопированиеФайла().
ПОДРОБНЕЕ
Подробнее познакомиться с примерами взаимодействия с FTP-сервером
можно в демонстрационной конфигурации «Примеры работы», которая
прилагается к книге.

В форме нашей демонстрационной обработки для обращения к FTP-ресурсу
используются следующие параметры, которые хранятся в соответствующих
реквизитах формы:
■■ Сервер – сервер, с которым осуществляется соединение, например
ftp.example.com;
■■ ИмяПользователя – имя пользователя на FTP-сервере;
■■ ПарольПользователя – пароль пользователя на FTP-сервере;
■■ КаталогНаСервере – путь на FTP-сервере до нужных файлов. Путь нужно
указывать только с прямыми слешами вместо обратных, например:
/TEST/;
■■ ЛокальныйКаталог – путь на локальном компьютере, определяющий, куда
копировать файлы, например E:\TEST\.

Глава 1. Интернет-технологии

153

Получить файлы с сервера
Предположим, нам нужно получить файлы определенного типа с FTP-сервера
через FTP-соединение. Для этого нужно создать FTP-соединение с сервером,
используя имя FTP-сервера, а также имя и пароль пользователя на этом
сервере. Затем найти нужные файлы по заданной маске (например, «*.tif»)
методом НайтиФайлы() в каталоге на сервере и скопировать найденные
файлы (объекты FTPФайл) в локальный каталог на компьютере пользователя
методом Получить() объекта FTPСоединение.

Обработчик команды для получения файлов с FTP-сервера может выглядеть
следующим образом (листинг 1.143).
Листинг 1.143. Процедура «ПолучитьФайлыССервера»
&НаКлиенте
Процедура ПолучитьФайлыССервера(Команда)

Сообщение = Новый СообщениеПользователю;
Попытка

// Установить соединение с FTP-сервером.

FTPСервер = Новый FTPСоединение(Сервер, , ИмяПользователя, ПарольПользователя);
Исключение

// Вывести сообщение об ошибке соединения с сервером.

Сообщение.Текст = ″Не удалось соединиться с сервером: ″ + Сервер;

Сообщение.Сообщить();

Сообщение.Текст = ОписаниеОшибки();

Сообщение.Сообщить();

Возврат;
КонецПопытки;
// Найти файлы в нужном каталоге по указанной маске.
МаскаФайлов = ″*.tif″;
МассивФайлов = FTPСервер.НайтиФайлы(КаталогНаСервере, МаскаФайлов);
Для Каждого Файл Из МассивФайлов Цикл

// Проверить, что это не каталог.

Если Файл.ЭтоФайл() Тогда


// Копировать файл в локальный каталог.

Сообщение.Текст = ″Считывается файл - ″ + Файл.Имя;

Сообщение.Сообщить();


FTPСервер.Получить(Файл.ПолноеИмя, ЛокальныйКаталог + Файл.Имя);

КонецЕсли;
КонецЦикла;
КонецПроцедуры

В первом параметре метода Получить() содержится относительный адрес
ресурса на FTP-сервере (полное имя FTP-файла), из которого будут прочитаны данные. Во втором – полное имя файла, в который будут помещены
данные полученного ресурса.

154

Технологии интеграции «1С:Предприятия 8.3»

Записать файлы на сервер
Теперь реализуем обратную операцию. Найдем файлы по заданной маске
на локальном компьютере и запишем их в каталог на FTP-сервере. Для этого
нужно создать FTP-соединение с сервером, используя имя FTP-сервера,
а также имя и пароль пользователя на этом сервере. И затем записать
найденные локальные файлы (объекты Файл) в каталог на сервере методом
Записать() объекта FTPСоединение.
Обработчик команды для записи файлов на FTP-сервер может выглядеть
следующим образом (листинг 1.144).
Листинг 1.144. Процедура «ЗаписатьФайлыНаСервер»
&НаКлиенте
Процедура ЗаписатьФайлыНаСервер(Команда)
// Найти файлы в локальном каталоге по указанной маске.
МаскаФайлов = ″*.tif″;
НачатьПоискФайлов(Новый ОписаниеОповещения(″ЗаписатьФайлыНаСерверЗавершение″, ЭтотОбъект),

ЛокальныйКаталог, МаскаФайлов);

КонецПроцедуры

Обратите внимание, что поиск файлов на компьютере пользователя мы
будем выполнять с помощью немодального метода НачатьПоискФайлов()
вместо модального НайтиФайлы(). Первым параметром в метод
НачатьПоискФайлов() мы передаем описание оповещения, указывающее
на экспортную процедуру ЗаписатьФайлыНаСерверЗавершение(),
которая будет вызвана по окончании поиска файлов (листинг 1.145). А также
мы передаем в эту процедуру локальный каталог, в котором будет производиться поиск, и маску для поиска файлов (например, «*.tif»).
Листинг 1.145. Процедура «ЗаписатьФайлыНаСерверЗавершение»
&НаКлиенте
Процедура ЗаписатьФайлыНаСерверЗавершение(МассивФайлов, Дополнительно ) Экспорт
Сообщение = Новый СообщениеПользователю;
Попытка

// Установить соединение с FTP-сервером.

FTPСервер = Новый FTPСоединение(Сервер, , ИмяПользователя, ПарольПользователя);
Исключение

// Вывести сообщение об ошибке соединения с сервером.

Сообщение.Текст = ″Не удалось соединиться с сервером: ″ + Сервер;

Сообщение.Сообщить();

Сообщение.Текст = ОписаниеОшибки();

Сообщение.Сообщить();

Глава 1. Интернет-технологии

155


Возврат;
КонецПопытки;
Для Каждого Файл Из МассивФайлов Цикл

// Записать файл в каталог на сервере.

Сообщение.Текст = ″Записывается файл - ″ + Файл.Имя;

Сообщение.Сообщить();


FTPСервер.Записать(ЛокальныйКаталог + Файл.Имя, КаталогНаСервере + Файл.Имя);
КонецЦикла;
КонецПроцедуры

В этой процедуре мы создаем FTP-соединение, обходим в цикле массив
найденных файлов, содержащихся в параметре МассивФайлов (объекты
Файл) изаписываем каждый файл в каталог на сервере методом Записать()
объекта FTPСоединение.

Копировать файлы с сервера
В этом примере мы рассмотрим возможность получения файлов с сервера
с помощью прямого копирования методом глобального контекста
НачатьКопированиеФайла().
Поскольку доступ к FTP-ресурсам будет осуществляться напрямую,
а не через FTP-соединение, то полный путь на сервере должен включать
помимо каталога на сервере также и имя самого сервера, и имя и пароль
пользователя на сервере вида: ftp://:@/.
Обработчик команды для копирования файлов с FTP-сервера может выглядеть следующим образом (листинг 1.146).
Листинг 1.146. Процедура «КопироватьФайлыССервера»
&НаКлиенте
Процедура КопироватьФайлыССервера(Команда)
ПутьНаСервере = ″ftp://″ + ИмяПользователя + ″:″ + ПарольПользователя

+ ″@″ + Сервер + КаталогНаСервере;
// Найти файлы в нужном каталоге по указанной маске.
МаскаФайлов = ″*.png″;
НачатьПоискФайлов(Новый ОписаниеОповещения(

″КопироватьФайлыССервераЗавершение″, ЭтотОбъект),

ПутьНаСервере, МаскаФайлов);

КонецПроцедуры

156

Технологии интеграции «1С:Предприятия 8.3»

Как и в предыдущем примере, поиск файлов на FTP-сервере мы будем
выполнять с помощью немодального метода НачатьПоискФайлов(). В этот
метод первым параметром мы передаем описание оповещения, указывающее
на экспортную процедуру КопироватьФайлыССервераЗавершение(),
которая будет вызвана по окончании поиска файлов (листинг 1.147). А также
мы передаем в эту процедуру полный путь на FTP-сервере для доступа
к файлам и маску для поиска файлов.
Листинг 1.147. Процедура «КопироватьФайлыССервераЗавершение»
&НаКлиенте
Процедура КопироватьФайлыССервераЗавершение(МассивФайлов, Дополнительно ) Экспорт
Сообщение = Новый СообщениеПользователю;

Для Каждого Файл Из МассивФайлов Цикл

Сообщение.Текст = ″Считывается файл - ″ + Файл.Имя;

Сообщение.Сообщить();


КопироватьФайлыВЛокальныйКаталог(Новый ОписаниеОповещения(

″КопироватьФайлыЛокальноЗавершение″, ЭтотОбъект), Файл.Имя);
КонецЦикла;
КонецПроцедуры

В этой процедуре мы обходим в цикле массив найденных файлов, содержащихся в параметре МассивФайлов, и для каждого файла вызываем
процедуру КопироватьФайлыВЛокальныйКаталог(), в которую передаем описание оповещения, указывающее на экспортную процедуру
КопироватьФайлыЛокальноЗавершение(), которая вернет процесс
копирования файлов к следующему шагу цикла, а также имя копируемого
файла.
Процедуру КопироватьФайлыВЛокальныйКаталог() заполним следующим образом (листинг 1.148).
Листинг 1.148. Процедура «КопироватьФайлыВЛокальныйКаталог»
&НаКлиенте
Процедура КопироватьФайлыВЛокальныйКаталог(Оповещение, ИмяФайла)
Сообщение = Новый СообщениеПользователю;
ПутьНаСервере = ″ftp://″ + ИмяПользователя + ″:″ + ПарольПользователя

+ ″@″ + Сервер + КаталогНаСервере;
ДопПараметры = Новый Структура(″ИмяФайла, Оповещение″, ИмяФайла, Оповещение);
Попытка

// Копировать файл в локальный каталог.

Глава 1. Интернет-технологии

157


НачатьКопированиеФайла(Новый ОписаниеОповещения(

″КопироватьФайлыЗавершение″, ЭтотОбъект, ДопПараметры.Оповещение),

ПутьНаСервере + ДопПараметры.ИмяФайла, ЛокальныйКаталог

+ ДопПараметры.ИмяФайла);
Исключение

Сообщение.Текст = ОписаниеОшибки();

Сообщение.Сообщить();

Возврат;
КонецПопытки;
КонецПроцедуры

В этой процедуре мы сначала упаковываем в структуру имя файла и имя
процедуры оповещения, содержащиеся в параметрах ИмяФайла и Оповещение. Затем вызываем процедуру НачатьКопированиеФайла(),
в которую передаем описание оповещения, указывающее на экспортную
процедуру КопироватьФайлыЗавершение(), которая будет вызвана по
окончании копирования файла (листинг 1.149). А также мы передаем в эту
процедуру полный путь на FTP-сервере для доступа к файлу и полный путь
на локальном компьютере, по которому будет скопирован FTP-ресурс.
Листинг 1.149. Процедура «КопироватьФайлыЗавершение»
&НаКлиенте
Процедура КопироватьФайлыЗавершение(СкопированныйФайл, ДополнительныеПараметры) Экспорт
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Файл скопирован - ″ + СкопированныйФайл;
Сообщение.Сообщить();
ВыполнитьОбработкуОповещения(ДополнительныеПараметры);
КонецПроцедуры

В этой процедуре после окончания копирования файла мы вызываем процедуру оповещения КопироватьФайлыЛокальноЗавершение(), имя
которой содержится в параметре ДополнительныеПараметры (листинг
1.150).
Листинг 1.150. Процедура «КопироватьФайлыЛокальноЗавершение»
&НаКлиенте
Процедура КопироватьФайлыЛокальноЗавершение(Результат, Дополнительно) Экспорт
Возврат;
КонецПроцедуры

В результате мы вернемся в цикл копирования найденных файлов
с FTP-сервера (см. листинг 1.147) в процедуре КопироватьФайлыССервераЗавершение().

158

Технологии интеграции «1С:Предприятия 8.3»

Электронная почта
Отправку и получение электронной почты из «1С:Предприятия» можно организовать с помощью объекта ИнтернетПочта. Объект ИнтернетПочта
позволяет работать с почтовыми серверами напрямую. Для отправки писем
используется протокол SMTP, а получать почту можно по протоколам POP3
и IMAP.
Для подключения к почтовому серверу необходимо знать его адрес, логин
и пароль пользователя, адрес отправителя и т. д. Настройки обращения
к почтовым серверам указываются в специализированном объекте ИнтернетПочтовыйПрофиль.
Ниже мы рассмотрим примеры отправки почты в виде форматированного
текста и получения почты в виде текста HTML, а также отправки и получения сообщений обмена с вложенными файлами.
ПОДРОБНЕЕ
Подробнее познакомиться с примерами работы по отправке и получению
почты можно в демонстрационной конфигурации «Примеры работы»,
которая прилагается к книге.

В форме нашей демонстрационной обработки для работы с электронной
почтой используются следующие параметры, которые хранятся в соответствующих реквизитах формы:
■■ СерверSMTP – сервер для отправки электронной почты, например
smtp.mail.ru;
■■ СерверPOP3 – сервер для получения электронной почты, например
pop3.mail.ru;
■■ СерверIMAP – сервер для получения электронной почты, например
imap.mail.ru;
■■ ИспользоватьIMAP – признак получения писем по протоколу IMAP;
■■ Пользователь – имя пользователя (логин) на почтовом сервере. Для
IMAP-сервера нужно указывать полное имя пользователя, например
@mail.ru;
■■ Пароль – пароль пользователя на почтовом сервере;
■■ АдресОтправителя – почтовый адрес, с которого будет отправляться почта;
■■ АдресПолучателя – почтовый адрес, на который будет отправляться почта;
■■ ФайлВложения – полное имя отправляемого файла;

Глава 1. Интернет-технологии

159

■■ Содержимое – содержимое отправляемого письма в виде форматированного документа;
■■ ТекстHTML – содержимое полученного письма в виде HTML-документа.

Отправить и получить почту
Прежде всего реализуем общую для всех примеров функцию ПолучитьПрофиль(), которая будет возвращать почтовый профиль (объект
ИнтернетПочтовыйПрофиль), заполненный введенными в форме обработки настройками (листинг 1.151).
Листинг 1.151. Функция «ПолучитьПрофиль»
&НаКлиенте
Функция ПолучитьПрофиль()
// Создать почтовый профиль.
Профиль = Новый ИнтернетПочтовыйПрофиль;
Профиль.АдресСервераSMTP = СерверSMTP;
Профиль.ПользовательSMTP = Пользователь;
Профиль.ПарольSMTP = Пароль;
Профиль.ТолькоЗащищеннаяАутентификацияSMTP = Истина;
Профиль.АдресСервераPOP3 = СерверPOP3;
Профиль.Пользователь = Пользователь;
Профиль.Пароль = Пароль;
Профиль.АдресСервераIMAP = СерверIMAP;
Профиль.ПользовательIMAP = Пользователь;
Профиль.ПарольIMAP = Пароль;
Возврат Профиль;
КонецФункции

Обратите внимание, что свойства профиля ПортSMTP, ПортIMAP и ПортPOP3
мы не указывали. По умолчанию значения этих свойств равны нулю. Это
значит, что для защищенных или незащищенных соединений будут использоваться стандартные порты. Поэтому, если нет какой-то специфической задачи
использовать нестандартные порты, значения этих свойств можно не задавать.
С помощью полученного почтового профиля выполняется подключение
к нужному почтовому серверу.
Теперь рассмотрим пример, когда тело сообщения формируется с помощью
форматированного документа, в него помещаются форматированный текст,
картинки, ссылки, специальные символы. Затем это сообщение отправляется,

160

Технологии интеграции «1С:Предприятия 8.3»

далее принимается с помощью почтового сервера и отображается в форме
обработки в HTML-документе.
Для этого в демонстрационную обработку нужно добавить реквизит типа
ФорматированныйДокумент и перетащить его в дерево элементов формы.
Для отображения командной панели форматированного документа в форме
нужно добавить над ним группу вида Командная панель и заполнить ее
свойство Источник команд реквизитом формы, который будет содержать этот
форматированный документ. А также в демонстрационную обработку нужно
добавить строковый реквизит, перетащить его в дерево элементов формы
и установить вид связанного с ним поля как Поле HTML документа.
Для отправки сообщений мы должны получить почтовый профиль
с помощью функции ПолучитьПрофиль(), описанной выше (см. листинг
1.151), и сформировать отправляемое сообщение. Затем подключиться
к SMTP-серверу методом Подключиться() объекта ИнтернетПочта, отправить сообщение методом Послать() и отключиться
от почтового сервера.
Для отправки сообщения, содержащего форматированный документ, можно
использовать следующий фрагмент кода (листинг 1.152).
Листинг 1.152. Процедура «ОтправитьПочту»
&НаКлиенте
Процедура ОтправитьПочту(Команда)
Перем HTML, Картинки;
// Получить почтовый профиль.
Профиль = ПолучитьПрофиль();
// Сформировать сообщение, содержащее форматированный документ.
Сообщение = Новый ИнтернетПочтовоеСообщение;
Сообщение.Тема = ″Форматированный документ ″;
Сообщение.Отправитель = АдресОтправителя;
Сообщение.Получатели.Добавить(АдресПолучателя);
// Создать вложения с картинками.
Содержимое.ПолучитьHTML(HTML, Картинки);
Для Каждого Картинка Из Картинки цикл

Вложение = Сообщение.Вложения.Добавить(Картинка.Значение.ПолучитьДвоичныеДанные());

Вложение.Идентификатор = Картинка.Ключ;

Вложение.Имя = Картинка.Ключ;

HTML = СтрЗаменить(HTML, Картинка.Ключ, ″cid:″ + Вложение.Идентификатор);
КонецЦикла;
ТекстСообщения = Сообщение.Тексты.Добавить(HTML, ТипТекстаПочтовогоСообщения.HTML);
Почта = Новый ИнтернетПочта;

Глава 1. Интернет-технологии

161

Сообщ = Новый СообщениеПользователю();
Попытка

Почта.Подключиться(Профиль);

// Отправить сообщение с форматированным текстом и картинками.

Почта.Послать(Сообщение);
Исключение

// Вывести сообщение об ошибке при подключении к серверу или при отправке сообщения обмена.

Сообщ.Текст = ″Ошибка при отправке файла: ″ + ФайлВложения;

Сообщ.Сообщить();

Сообщ.Текст = ОписаниеОшибки();

Сообщ.Сообщить();

Возврат;
КонецПопытки;
Сообщ.Текст = ″Сообщение отправлено. ″;
Сообщ.Сообщить();
Почта.Отключиться();
КонецПроцедуры

Сначала мы получаем почтовый профиль, затем формируем сообщение
обмена для отправки. При этом мы указываем отправителя в свойстве
Отправитель объекта ИнтернетПочтовоеСообщение и добавляем
получателя в список получателей сообщения – Сообщение.Получатели.
Добавить(АдресПолучателя).

Для отправки содержимого форматированного документа (реквизит Содержимое) используется его метод ПолучитьHTML(), который получает
HTML-составляющие форматированного документа – массив картинок
и HTML-текст. Затем полученные картинки добавляются как вложения
в почтовое сообщение и сопоставляются с текстом письма путем установки свойства Идентификатор объекта ИнтернетПочтовоеВложение.
HTML-текст добавляется в массив текстов почтового сообщения с указанием
типа текста ТипТекстаПочтовогоСообщения.HTML. Остальная работа
по отправке сообщения выполняется с помощью методов Подключиться()
и Послать() объекта ИнтернетПочта.
Для получения сообщений мы должны получить почтовый профиль
с помощью функции ПолучитьПрофиль(), описанной ранее (см. листинг
1.151). Затем, используя этот профиль, подключиться к почтовому серверу
для получения почты по протоколу IMAP или POP3. Тип протокола интернетпочты можно передать вторым параметром в метод Подключиться()
объекта ИнтернетПочта или использовать протокол по умолчанию (SMTP –
для отправки почты, POP3 – для получения почты).

162

Технологии интеграции «1С:Предприятия 8.3»

При получении писем надо учитывать, что при большом потоке писем полная
их загрузка может занимать много времени. Чтобы решить эту проблему,
существует возможность не загружать полностью все почтовые сообщения,
а первоначально загружать только их заголовки.
С помощью метода ПолучитьЗаголовки() объекта ИнтернетПочта
можно получать с сервера только заголовки сообщений. Благодаря этому,
например, можно получить информацию о теме, отправителе и т. д. и решить,
принимать это письмо, оставить его или сразу удалить.
Данный метод возвращает массив объектов типа ИнтернетПочтовоеСообщение. Каждый объект содержит информацию из заголовка сообщения.
Вложения и тексты с сервера не принимаются.
Для получения сообщения, содержащего форматированный документ,
и отображения его в поле HTML-документа можно использовать следующий
фрагмент кода (листинг 1.153).
Листинг 1.153. Процедура «ПолучитьПочту»
&НаКлиенте
Процедура ПолучитьПочту(Команда)
// Получить почтовый профиль.
Профиль = ПолучитьПрофиль();
Сообщ = Новый СообщениеПользователю();
Почта = Новый ИнтернетПочта;
Попытка

Если ИспользоватьIMAP Тогда

Почта.Подключиться(Профиль, ПротоколИнтернетПочты.IMAP);

Иначе

Почта.Подключиться(Профиль, ПротоколИнтернетПочты.POP3);

КонецЕсли;

Исключение

// Вывести сообщение об ошибке при подключении к серверу.

Сообщ.Текст = ″Ошибка при подключении или приеме″ + ОписаниеОшибки();

Сообщ.Сообщить();

Возврат;
КонецПопытки;
// Получить заголовки сообщений с отбором из почтового ящика.
ПараметрыОтбораIMAP = Новый Структура;
ПараметрыОтбораIMAP.Вставить(″Прочитанные″, Ложь);
ЗаголовкиСообщений = Почта.ПолучитьЗаголовки(ПараметрыОтбораIMAP);
КоличествоСообщений = ЗаголовкиСообщений.Количество();
Если КоличествоСообщений = 0 Тогда

Сообщ.Текст = ″Сообщений в почтовом ящике нет.″;

Сообщ.Сообщить();

Почта.Отключиться();

Возврат;
КонецЕсли;

Глава 1. Интернет-технологии

163

// Создать соответствие для установки флагов сообщений.
ФлагиСообщенийIMAP = Новый Соответствие;
// Получить полностью сообщения.
МассивСообщений = Почта.Выбрать(Ложь, ЗаголовкиСообщений);
Для Индекс = 0 По КоличествоСообщений - 1 Цикл




ФлагСообщения = Новый ФлагиИнтернетПочтовогоСообщения();
ФлагСообщения.Удаленное = Истина;
ФлагиСообщенийIMAP.Вставить(МассивСообщений[Индекс].Идентификатор[0], ФлагСообщения);


Сообщ.Текст = ″Принято сообщение: ″ + МассивСообщений[Индекс].Тема;

Сообщ.Сообщить();


// Вывести содержимое письма в HTML документ для последнего сообщения.

Если Индекс = КоличествоСообщений - 1 Тогда

Для каждого Элемент Из МассивСообщений[Индекс].Тексты Цикл

Если Элемент.ТипТекста = ТипТекстаПочтовогоСообщения.HTML Тогда

// Отобразить тело сообщения в HTML документе.

ТекстHTML = Элемент.Текст;

Если Найти(ТекстHTML, ″″) = 0 Тогда

ТекстHTML = ″″ + ТекстHTML + ″″;

КонецЕсли;


Вложения = Новый Массив;

// Обработать вложения, чтобы правильно сформировать HTML.

Для каждого Вложение Из МассивСообщений[Индекс].Вложения Цикл

Ид = ″cid:″ + Вложение.Идентификатор;

Если Найти(ТекстHTML, Ид) 0 Тогда

Вложения.Добавить(Вложение);

КонецЕсли;

КонецЦикла;

КонецЕсли;

КонецЦикла;


Индекс = 0;

Для каждого Вложение Из Вложения Цикл

// Записать файл картинки во временный файл.

ФайлОбмена = Вложение.Данные;

ИмяФайла = ″c:\temp\pict_″ + Строка(Индекс) + ″.png″;

ФайлОбмена.Записать(ИмяФайла);

// Отобразить картинку в HTML.

Ид = ″″″cid:″ + Вложение.Идентификатор + ″″″″;

ТекстHTML = СтрЗаменить(ТекстHTML, Ид, ″″″″ + ИмяФайла + ″″″″);

Индекс = Индекс + 1;

КонецЦикла;


КонецЕсли;
КонецЦикла;
Почта.УстановитьФлагиСообщений(ФлагиСообщенийIMAP);
Почта.Отключиться();
КонецПроцедуры

164

Технологии интеграции «1С:Предприятия 8.3»

В данном примере для получения почты мы будем использовать
протокол IMAP, поэтому с помощью метода ПолучитьЗаголовки()
мы можем получить с сервера массив заголовков только тех сообщений,
которые удовлетворяют параметрам отбора, указанным нами в структуре
ПараметрыОтбораIMAP. Эту структуру мы передаем в метод ПолучитьЗаголовки(). В нашем примере мы используем отбор по непрочитанным
письмам – ПараметрыОтбораIMAP.Вставить("Прочитанные", Ложь).
Если же для получения почты используется протокол POP3, то параметры
отбора игнорируются и с сервера получаются заголовки всех сообщений.

После этого массив отобранных заголовков сообщений мы передаем вторым
параметром в метод Выбрать() объекта ИнтернетПочта и получаем
нужные нам сообщения целиком (вместе с вложениями) в массиве МассивСообщений.
Затем мы создаем соответствие ФлагиСообщенийIMAP для установки
флагов сообщений, которое мы будем заполнять в цикле обхода сообщений
и передавать в метод УстановитьФлагиСообщений(). Этот метод
доступен также только при подключении по протоколу IMAP.
В цикле обхода массива сообщений мы добавляем в соответствие
элемент, ключом которого является идентификатор почтового сообщения
(МассивСообщений[Индекс].Идентификатор[0]), а значением – объект
ФлагиИнтернетПочтовогоСообщения. Свойство Удаленное этого
объекта мы устанавливаем в значение Истина.

Для отображения содержимого полученного сообщения используется
элемент формы ТекстHTML вида Поле HTML документа, связанный с соответствующим строковым реквизитом формы. В этот реквизит помещается
HTML-текст прочитанного сообщения.
Для отображения картинок в HTML-документе мы заполняем массив
Вложения. В цикле обхода этого массива мы сохраняем каждую картинку во
временный файл и сопоставляем этот файл с текстом HTML.
В заключение устанавливаем флаги прочитанных сообщений методом УстановитьФлагиСообщений() объекта ИнтернетПочта, в результате чего
эти сообщения удаляются с почтового сервера.
Итак, после отправки и получения почты форма обработки будет иметь
следующий вид (рис. 1.42).

Глава 1. Интернет-технологии

165

Рис. 1.42. Работа с почтой

Отправить и получить сообщение обмена
Теперь рассмотрим пример, позволяющий отправлять почтовые сообщения
вместе с вложенными в них файлами обмена.
Процедуру для отправки сообщения заполним следующим образом (листинг
1.154).
Листинг 1.154. Процедура «ОтправитьСообщение»
&НаКлиенте
Процедура ОтправитьСообщение(Команда)
// Получить почтовый профиль.
Профиль = ПолучитьПрофиль();
// Сформировать сообщение обмена.
Сообщение = Новый ИнтернетПочтовоеСообщение;
Сообщение.Тема = ″СООБЩЕНИЕ_ОБМЕНА: ″ + ФайлВложения;
Сообщение.Отправитель = АдресОтправителя;
Сообщение.Получатели.Добавить(АдресПолучателя);

166

Технологии интеграции «1С:Предприятия 8.3»

Если ФайлВложения ″″ Тогда

ФайлСообщения = Новый Файл(ФайлВложения);
// Создать вложение с файлом обмена.

Сообщение.Вложения.Добавить(ФайлВложения, ФайлСообщения.Имя);
КонецЕсли;
Почта = Новый ИнтернетПочта;
Сообщ = Новый СообщениеПользователю();
Попытка

Почта.Подключиться(Профиль);
// Отправить сообщение с файлом обмена.

Почта.Послать(Сообщение);
Исключение

// Вывести сообщение об ошибке при подключении к серверу или при отправке сообщения обмена.
Сообщ.Текст = ″Ошибка при отправке файла: ″ + ФайлВложения;

Сообщ.Сообщить();

Сообщ.Текст = ОписаниеОшибки();

Сообщ.Сообщить();

Возврат;
КонецПопытки;
Сообщ.Текст = ″Отправлен файл: ″ + ФайлВложения + ″ на адрес: ″ + АдресПолучателя;
Сообщ.Сообщить();
Почта.Отключиться();
КонецПроцедуры

Сначала мы получаем почтовый профиль, затем формируем сообщение
обмена для отправки. При этом мы указываем отправителя в свойстве
Отправитель объекта ИнтернетПочтовоеСообщение и добавляем
получателя в список получателей сообщения – Сообщение.Получатели.
Добавить(АдресПолучателя). А также мы помечаем тему сообщения как
«СООБЩЕНИЕ_ОБМЕНА: », чтобы при приеме почты, при получении заголовков сообщений, отобрать нужные нам сообщения по теме.
И если файл вложения был выбран, то добавляем его в коллекцию вложений
сообщения – Сообщение.Вложения.Добавить(ФайлВложения, ФайлСообщения.Имя).

Выбор файла вложения будет осуществляться по нажатию специальной
кнопки. В обработчике соответствующей команды мы будем показывать
диалог выбора файлов с помощью немодального метода Показать() объекта
ДиалогВыбораФайла. В этот метод первым параметром мы передаем
описание оповещения, указывающее на экспортную процедуру ПослеВыбораФайлов(), которая будет вызвана по закрытии диалога выбора файлов.
Обработчик команды для выбора файла вложения заполним следующим
образом (листинг 1.155).

Глава 1. Интернет-технологии

167

Листинг 1.155. Процедура «ВыбратьФайлВложения»
&НаКлиенте
Процедура ВыбратьФайлВложения(Команда)
ВыборФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ВыборФайла.Заголовок = ″Выбор файла″;
ВыборФайла.Показать(Новый ОписаниеОповещения(″ПослеВыбораФайлов″, ЭтотОбъект));
КонецПроцедуры

В процедуру-обработчик оповещения ПослеВыбораФайлов() в параметре
ВыбранныеФайлы передается массив выбранных имен файлов (листинг
1.156).
Листинг 1.156. Процедура «ПослеВыбораФайлов»
&НаКлиенте
Процедура ПослеВыбораФайлов(ВыбранныеФайлы, ДопПараметры) Экспорт
Если ВыбранныеФайлы = Неопределено Тогда

Возврат;
КонецЕсли;
ФайлВложения = ВыбранныеФайлы[0];
КонецПроцедуры

Теперь реализуем получение отправленного сообщения с почтового сервера.
Процедуру для получения сообщения заполним следующим образом
(листинг 1.157).
Листинг 1.157. Процедура «ПолучитьСообщение»
&НаКлиенте
Процедура ПолучитьСообщение(Команда)
// Получить почтовый профиль.
Профиль = ПолучитьПрофиль();
Сообщ = Новый СообщениеПользователю();
Почта = Новый ИнтернетПочта;
Попытка

Если ИспользоватьIMAP Тогда

Почта.Подключиться(Профиль, ПротоколИнтернетПочты.IMAP);

Иначе

Почта.Подключиться(Профиль, ПротоколИнтернетПочты.POP3);

КонецЕсли;

Исключение

168

Технологии интеграции «1С:Предприятия 8.3»

// Вывести сообщение об ошибке при подключении к серверу или при приеме сообщения обмена.

Сообщ.Текст = ″Ошибка при подключении к серверу или приеме″ + ОписаниеОшибки();

Сообщ.Сообщить();

Возврат;
КонецПопытки;
// Получить заголовки сообщений из почтового ящика.
МассивЗаголовков = Новый Массив;
ЗаголовкиСообщений = Почта.ПолучитьЗаголовки();
Если ЗаголовкиСообщений.Количество() = 0 Тогда

Сообщ.Текст = ″Сообщений в почтовом ящике нет.″;

Сообщ.Сообщить();

Почта.Отключиться();

Возврат;
КонецЕсли;
// Отфильтровать по теме массив заголовков сообщений обмена.
Для Индекс = 0 По ЗаголовкиСообщений.Количество() - 1 Цикл

ЧастьТемы = ″СООБЩЕНИЕ_ОБМЕНА: ″;

Если Лев(ЗаголовкиСообщений[Индекс].Тема, СтрДлина(ЧастьТемы)) = ЧастьТемы Тогда

МассивЗаголовков.Добавить(ЗаголовкиСообщений[Индекс]);

КонецЕсли;
КонецЦикла;
Если МассивЗаголовков.Количество() = 0 Тогда

Сообщ.Текст = ″Сообщений обмена в почтовом ящике нет.″;

Сообщ.Сообщить();

Почта.Отключиться();

Возврат;
КонецЕсли;
// Получить полностью сообщения обмена, которые впоследствии будут удалены с сервера.
МассивСообщенийОбмена = Почта.Выбрать(Истина, МассивЗаголовков);

ФайлВложения = ″″;
Для Индекс = 0 По МассивСообщенийОбмена.Количество() - 1 Цикл

Если МассивСообщенийОбмена[Индекс].Вложения[0].Имя > ФайлВложения Тогда

// Выбрать сообщение с последним номером сообщения обмена.

ФайлВложения = МассивСообщенийОбмена[Индекс].Вложения[0].Имя;

ИндексСообщения = Индекс;

КонецЕсли;
КонецЦикла;
Если ИндексСообщения Неопределено Тогда

// Записать файл обмена во временный файл.

ФайлОбмена = МассивСообщенийОбмена[ИндексСообщения].Вложения[0];

ФайлОбмена.Данные.Записать(″c:\temp\″ + ФайлОбмена.ИмяФайла);

Сообщ.Текст = ″Принят файл обмена данными: ″

+ ФайлВложения + ″ с адреса: ″ + АдресОтправителя;

Сообщ.Сообщить();
КонецЕсли;
Почта.Отключиться();

КонецПроцедуры

Глава 1. Интернет-технологии

169

В этой процедуре мы подключаемся к серверу для получения почты, затем
с помощью метода ПолучитьЗаголовки()
получаем с сервера
массив заголовков всех сообщений (объектов типа ИнтернетПочтовоеСообщение). Затем из массива ЗаголовкиСообщений в массив
МассивЗаголовков отфильтровываем только те сообщения, тема которых
начинается с префикса «СООБЩЕНИЕ_ОБМЕНА: ».
Если МассивЗаголовков пуст, то, значит, сообщений обмена на сервере
нет, поэтому мы отключаемся от почтового сервера и заканчиваем работу
процедуры. Иначе мы передаем массив заголовком вторым параметром
в метод Выбрать()объекта ИнтернетПочта и получаем нужные нам
сообщения обмена целиком (вместе с вложениями) в массиве МассивСообщенийОбмена. Первым параметром в этот метод мы передаем Истина
(это значение по умолчанию). Это означает, что полученные сообщения
обмена будут удалены с сервера.
В заключение среди всех сообщений обмена мы находим сообщение
с последним номером файла вложения, сохраняем его на компьютер и отключаемся от почтового сервера.
Кроме того, можно заключить процедуру получения сообщений в операторы
Попытка … Исключение, что позволит продолжить получение, если при
приеме определенного сообщения произошла ошибка. Также можно поместить в форме индикатор для графического отображения процесса получения
сообщений.

170

Технологии интеграции «1С:Предприятия 8.3»

Глава 2.
Внешние
источники данных

В процессе работы прикладных решений «1С:Предприятия» может возникнуть потребность получить и изменить данные, хранящиеся во внешних
базах данных, созданных с помощью различных сторонних СУБД (Microsoft
SQL Server, PostgreSQL, MySQL и т. п.). Эти базы данных могут быть как
реляционными, так и аналитическими.
В этой главе будет подробно, с примерами, рассмотрена работа с реляционными источниками данных, в которых информация представлена в виде
взаимосвязанных двумерных таблиц, каждая из которых содержит информацию об объектах определенного типа.
Данные в аналитических базах данных (OLAP-системах) содержат агрегированную информацию в виде многомерных пространств, так называемых
кубов. Работа с аналитическими источниками данных в целом аналогична
получению данных из реляционных баз данных. Этот вопрос более подробно
рассмотрен в документации «1С:Предприятия» в разделе «Глава 23. Внешние
источники данных – Работа с внешним источником данных OLAP».
Для доступа к внешним данным в платформе «1С:Предприятие» существует
объект конфигурации ВнешниеИсточникиДанных. В состав внешнего
источника данных могут входить таблицы и функции реляционного источника данных и кубы аналитического источника данных.

172

Технологии интеграции «1С:Предприятия 8.3»

Работа с реляционными внешними
источниками данных
Для того чтобы использовать информацию из внешних источников данных
в прикладном решении «1С:Предприятия», нужно:
■■ Изучить структуру внешнего источника данных и понять, какая информация (таблицы и поля или функции) необходима для работы алгоритмов
или отчетов в «1С:Предприятии».
■■ Создать объект ВнешнийИсточникДанных, содержащий необходимые подчиненные таблицы и поля. Это можно либо сделать вручную,
либо заполнить структуру данных этого объекта из внешнего источника
данных.
■■ Реализовать использование созданных объектов в прикладном решении.
■■ Выполнить настройку параметров подключения к внешнему источнику
данных в той сети, где будет использоваться прикладное решение. Эти
параметры могут отличаться от тех, которые использовались при загрузке
структуры внешнего источника данных.
ВНИМАНИЕ!
Параметры доступа к внешнему источнику данных, которые были заданы
в конфигураторе, не будут использованы системой в режиме 1С:Предприятие.
В общем случае при подключении к внешнему источнику данных
в конфигураторе могут использоваться одни параметры, а при подключении
в режиме 1С:Предприятие – другие.

Общая информация
Объект, описывающий внешний источник данных, подключенный к реляционной базе данных, может быть использован в «1С:Предприятии»
следующим образом:
■■
■■
■■
■■
■■

в качестве источника данных для запросов;
в качестве источника данных в системе компоновки данных;
в качестве источника для динамических списков;
входить в состав общих реквизитов;
записи таблиц могут отображаться в управляемых формах
«1С:Предприятия» (для объектов внешних источников данных не поддерживается использование обычных форм);
■■ таблицы внешнего источника данных могут выступать в качестве типов
реквизитов информационной базы;

Глава 2. Внешние источники данных

173

■■ к таблицам (и полям) внешних источников данных можно применять
права доступа и накладывать ограничения доступа к данным;
■■ доступ к таблицам и полям возможен из встроенного языка;
■■ таблица внешнего источника данных может входить в состав подсистем;
■■ таблица внешнего источника данных может входить в состав функциональных опций.
ВНИМАНИЕ!
Механизм внешних источников данных не должен использоваться для
доступа к базам данных «1С:Предприятия», так как модель данных
«1С:Предприятия» не рассчитана на работу с данными на уровне
физических структур хранения в системах управления базами данных
(СУБД). Возможность записи во внешние источники данных не должна
использоваться для замены штатного механизма хранения данных
прикладного решения.

Возможности системы «1С:Предприятие» максимально учитываются при
использовании в качестве источника внешних данных следующих СУБД
(свойство Тип СУБД параметров соединения с источником данных):
■■ Microsoft SQL Server,
■■ IBM DB2,
■■ PostgreSQL,
■■ Oracle Database,
■■ MySQL.
При использовании других СУБД возможности работы с внешними источниками данных зависят от самой СУБД.
ПРИМЕЧАНИЕ
При работе с внешним источником данных могут возникать ошибки
в том случае, если свойство Тип СУБД параметров соединения с внешним
источником данных содержит значение, которое не соответствует реально
используемой системе.
ПОДРОБНЕЕ
Более подробно получение информации из реляционного источника
данных описано в документации «1С:Предприятия» в разделе «Глава
24. Внешние источники данных – Работа с реляционными внешними
источниками данных».

174

Технологии интеграции «1С:Предприятия 8.3»

Для получения доступа к внешним источникам данных используется механизм ODBC. Данные внешних источников данных доступны как для чтения,
так и для записи.
Для подключения к внешнему источнику данных следует сформировать
строку подключения, которая может содержать в себе либо все параметры
подключения, необходимые для выбранного драйвера ODBC, либо указание
на сформированное описание источника данных DSN (Data Source Name).
Подробнее об этом рассказывается в следующем разделе.

Строка соединения
Для подключения к внешнему источнику данных с помощью DSN можно
воспользоваться специальными утилитами администрирования ODBC,
которые могут сформировать описание источника данных, и затем указать
в параметрах соединения специальную конструкцию вида DSN=.
Описания источника данных могут быть пользовательскими и системными. Пользовательские описания источников данных доступны на данном
компьютере и только тому пользователю, который создавал этого описание.
Системное описание создается системным администратором и доступно
всем пользователем данного компьютера.
Чтобы в ОС Windows запустить эту утилиту, нужно выполнить команду
Панель управления > Администрирование > Источники данных (ODBC). При этом
открывается утилита администрирования, соответствующая версии операционной системы (рис. 2.1, 2.2).
ПОДРОБНЕЕ
Более подробно процесс создания DSN будет показан в разделе «Примеры
использования».

Следует помнить, что в 64-разрядной версии ОС Windows существуют
разные утилиты администрирования для 32-разрядного и 64-разрядного
ODBC. Поэтому для создания описания источника данных следует использовать версию утилиты администрирования, соответствующую версии
системы «1С:Предприятия», которая будет исполнять запросы к внешнему
источнику данных. Так, например, если обращение к ODBC выполняется из
64-разрядной версии «1С:Предприятия», то надо использовать 64-разрядную
версию утилиты администрирования.

Глава 2. Внешние источники данных

Рис. 2.1. Создание источника данных ODBC

Рис. 2.2. Создание источника данных ODBC

175

176

Технологии интеграции «1С:Предприятия 8.3»

На 64-разрядной версии ОС Windows утилиты администрирования расположены следующим образом:
■■ 64-разрядная версия: %SYSTEMROOT%\System32\odbcad32.exe;
■■ 32-разрядная версия: %SYSTEMROOT%\SysWOW64\odbcad32.exe.
В ОС Linux эта утилита называется ODBCConfig и доступна при установке
соответствующего пакета (зависит от версии ОС Linux).
При описании полной строки соединения параметры строки соединения
должны записываться парами КлючевоеСлово=Значение и разделяться
символом «;». Для упрощения формирования строки соединения можно
воспользоваться сайтом http://www.connectionstrings.com/.
Таким образом, при подключении к внешнему источнику данных строка
соединения может иметь вид:
DRIVER=MySQL ODBC 8.0 Unicode Driver;User ID=root;
Password=password;Data Source=localhost;Database=spa

Или же при подключении с помощью предварительно настроенного источника данных (DSN):
DSN=MyDB

Также следует помнить, что соединение с внешним источником данных
следует указывать не только в конфигураторе (если используется механизм
импорта структуры таблиц из внешнего источника данных), но и в режиме
«1С:Предприятие» для получения собственно данных.

Редактирование структуры внешнего источника данных
Для работы с внешними источниками данных предназначена ветвь дерева
конфигурации Внешние источники данных. В этой группе объектов можно
добавить внешний источник данных, определить, какие таблицы будет содержать этот источник, и затем описать состав полей (и их свойства), которые
будут храниться в каждой таблице.
При этом в конфигурации может быть описано меньшее количество таблиц
и полей в них, чем есть в реальной базе данных, но нельзя задать таблицу
или поле, которое отсутствует в реальной базе данных.
Создание структуры внешнего источника данных возможно как в ручном
режиме, так и при загрузке этой структуры с помощью специального
конструктора. Использование конструктора наиболее просто и надежно.
Платформа сама безошибочно настроит все основное, что нужно для работы
с внешними источниками данных. Но бывают такие ситуации, когда требу-

Глава 2. Внешние источники данных

177

ется добавить или изменить состав таблиц и полей источника данных
вручную. Такой вариант тоже возможен, хотя он более трудоемкий и чреват
ошибками.
Создание таблиц внешнего источника данных
с помощью конструктора
Чтобы загрузить список таблиц и полей из внешней базы данных, нужно
в окне редактирования свойств внешнего источника на закладке Данные
нажать кнопку Добавить и выбрать пункт Выбрать из списка таблиц внешнего источника данных в конструкторе таблиц внешнего источника данных
(рис. 2.3).

Рис. 2.3. Загрузка структуры таблиц из внешнего источника данных

Затем нужно указать строку соединения с внешней базой данных, воспользовавшись окном Подключение к источнику данных. Например, можно
подключиться с помощью ранее созданного DSN, указав в строке соединения
имя источника данных ODBC (рис. 2.4).
После того как подключение к внешней базе успешно выполнено, будет
открыт список с перечнем таблиц и полей подключенного источника данных.
Из этого списка можно выбрать необходимые таблицы и поля, на основе
которых платформа создаст соответствующую структуру объектов, описывающих текущий источник данных (рис. 2.5).

178

Технологии интеграции «1С:Предприятия 8.3»

Рис. 2.4. Подключение к источнику данных ODBC

Рис. 2.5. Конструктор таблиц внешнего источника данных

При получении структуры внешнего источника данных платформа автоматически пытается определить, какие данные расположены в таблице: объектные
или необъектные. Таблица будет считаться объектной, если для нее указано
только одно ключевое поле, и необъектной в противном случае. Если платформа ошиблась с указанием ключевых полей, можно вручную изменить
состав полей, которые образуют ключ таблицы. Если таблица определена
как объектная, у нее можно указать поле, которое формирует представление
данных такого типа. Поле представления необходимо указать вручную.
Затем платформа преобразует типы полей таблиц внешнего источника
данных к типам «1С:Предприятия»: число, строка, дата, булево, уникальный
идентификатор, двоичные данные и типы, связанные с объектными таблицами источников данных. Если платформа неправильно выбрала тип колонки
таблицы (например, ссылочное поле), его можно изменить вручную.
Кроме того, в конструкторе загрузки структуры внешнего источника данных
можно настроить и другие параметры:
■■ имя объекта в конфигурации – колонка Имя в конфигурации;
■■ тип загружаемого реквизита – колонка Тип;

Глава 2. Внешние источники данных

179

■■ указать поля, входящие в состав ключа (для объектных данных), – колонка
Поле ключа;
■■ указать поле, хранящее представление объекта, – колонка Поле представления;
■■ указать поле, хранящее версию объекта (для осуществления оптимистической блокировки данных), – колонка Поле версии;
■■ указать, что таблица или поле доступны только на чтение, – колонка
Только чтение;
■■ указать возможность записывать значение NULL в поле – колонка Разрешить NULL;
■■ задать значение заполнения для реквизита – колонка Значение заполнения.
После настройки указанных параметров, отметки нужных таблиц и полей
и нажатия кнопки Готово произойдет загрузка структуры внешнего источника
данных в конфигурацию.
ПОДРОБНЕЕ
Более подробно загрузка структуры внешнего источника данных будет
показана в разделе «Примеры использования».

Создание таблиц внешнего источника данных вручную
Помимо этого список таблиц внешнего источника данных можно полностью заполнить вручную в конфигураторе. То есть таблицы и их поля можно
создать обычным образом, как подчиненные внешнему источнику объекты
конфигурации, и установить их свойства.
При этом нужно иметь в виду, что в одной таблице прикладного решения
могут быть размещены данные только из одной физической таблицы
сторонней СУБД.
Обратим внимание на наиболее важные свойства таблиц внешнего источника данных. Они собраны на закладке Данные окна редактирования свойств
таблицы (рис. 2.6).
При ручном заполнении списка таблиц и полей нужно следить, чтобы их
свойство Имя в источнике данных в точности соответствовало имени таблиц
и полей внешнего источника данных. В отличие от этого при заполнении
с помощью конструктора основные свойства таблиц и полей определяются
автоматически.

180

Технологии интеграции «1С:Предприятия 8.3»

Рис. 2.6. Свойства таблицы внешнего источника данных

Также очень важно правильно установить свойство Тип данных таблицы.
Нужно отметить, какие данные хранятся в этой таблице: объектные или
необъектные. Если в таблице можно выделить одно поле, которое однозначно определяет запись в таблице, значит, таблица хранит объектные
данные. Ближайшим аналогом таких таблиц служит справочник. Если запись
в таблице идентифицируется несколькими ключевыми полями, то такая
таблица содержит необъектные данные. Ближайшим аналогом таких таблиц
служит регистр сведений. Ключевые поля таблицы необходимо указать
в свойстве Поля ключа.
Для таблиц, содержащих объектные данные, можно указать поле, которое
будет выступать в качестве представления объекта. Это можно сделать
с помощью свойства Поле представления.
В зависимости от того, какой объект СУБД описывает создаваемая таблица,
следует корректно установить свойство Вид таблицы. Если свойство установлено в значение Таблица, то объект конфигурации будет выступать аналогом
реальной таблицы базы данных или представления (view).

Глава 2. Внешние источники данных

181

Для таблицы вида Выражение следует указать свойство Выражение в источнике данных. В простейшем случае это будет вызов функции, возвращающей
табличные данные, например dbo.ufnGetContactInformation(&1).
Если в это поле записывается выражение, то рекомендуется указать его
в скобках для минимизации различных конфликтов при реальном выполнении запроса к СУБД.
Свойство Только чтение отвечает за возможность записи в таблицу внешнего
источника данных. Это свойство автоматически устанавливается в значение
Истина для таблиц, основанных на представлениях (view) и функциях.
Для таблицы, в которую допустима запись информации, флажок Только
чтение должен быть сброшен. При записи в таблицу важны следующие
свойства:
■■ Уровень изоляции транзакций – определяет уровень изоляции транзакций,
который будет устанавливаться при неявных транзакциях записи в эту
таблицу.
■■ Поле версии данных – в данном свойстве указывается поле внешнего
источника данных, которое увеличивает свое значение автоматически
при каждой записи в данную таблицу.Если поле указано, то система при
интерактивном изменении объекта опирается на значение этого поля.
При записи выполняется сравнение значений этого поля в базе данных
и в форме: если значения различаются, значит, объект изменен. Если
поле не указано, то при интерактивном открытии формы происходит
считывание объекта в память, а при записи происходит повторное чтение
объекта и сравнение с копией в памяти. Если две копии различаются,
значит, объект изменен.
■■ Поля блокировки данных – указывает поля, по которым можно выполнять
блокировку данных.
■■ Вводится на основании – указывает объекты конфигурации, на основании
которых могут создаваться записи в таблице.
■■ Режим управления блокировкой данных – указывает, какой режим управления блокировками будет применяться при записи в данную таблицу.
Свойства полей
Затем у таблиц внешнего источника данных вида Таблица нужно указать
список полей, которые описывают, какие данные физической таблицы будут
доступны из прикладного решения. Свойство поля Имя в источнике данных
служит для указания соответствия между полем прикладного решения
и полем физической таблицы. Поэтому значение в этом свойстве должно
в точности соответствовать имени колонки этой таблицы.

182

Технологии интеграции «1С:Предприятия 8.3»

Необходимо помнить, что если значение, находящееся в свойстве Имя
в источнике данных, заключено в одинарные кавычки, то в SQL-запрос
к базе данных это значение попадает без преобразований вне зависимости от
состава символов. Если значение в поле не заключено в одинарные кавычки,
то в SQL-запрос к базе данных это значение попадет, заключенное в двойные
кавычки, если в имени содержатся спецсимволы.
В свойстве Тип нужно указать тип поля. Выбор ограничен следующими
типами:
■■ Число;
■■ Строка;
■■ Дата;
■■ Булево;
■■ УникальныйИдентификатор;
■■ ДвоичныеДанные;
■■ типы, которые определяются таблицами внешних источников данных.
Если поле имеет составной тип, то в таком составном типе могут участвовать
только типы Число, Строка, Дата, Булево.

Свойство Только чтение указывает, может ли выполняться запись в данное
поле таблицы. Это свойство следует устанавливать в значение Истина для
полей, рассчитываемых автоматически (AUTOINCREMENT), вычисляемых
полей и т. п.
Однако при работе с внешним источником данных иногда возникает необходимость выполнять запись полей, которые в обычной работе предназначены
только для чтения, или наоборот. Для этого можно временно изменить (как
сузить, так и расширить) список записываемых полей с помощью методов
ПолучитьИзменяемыеПоля() и УстановитьИзменяемыеПоля(). Соответствующий пример будет показан в разделе «Примеры использования».
Таким образом, состояние свойства Только чтение описывает поведение поля
по умолчанию, а с помощью указанных методов можно в редких случаях
изменять поведение поля.
Свойство Разрешить NULL указывает, можно ли в поле таблицы записать
значение NULL. Это свойство следует устанавливать в значение Истина для
всех полей, кроме ключевых и тех полей, которые во внешнем источнике
описаны как NOT NULL.

Глава 2. Внешние источники данных

183

Работа с функциями внешнего источника данных
Иногда в «1С:Предприятии» требуется использовать специфические возможности различных СУБД, такие как функции, последовательности, хранимые
процедуры СУБД, к которым подключен внешний источник данных прикладного решения. В качестве примера можно привести объектную таблицу
СУБД, ключ которой получается из последовательности. Функции и процедуры могут ничего не возвращать (процедура), а также возвращать одиночное
значение или таблицу.
Общая информация
Функции внешнего источника данных в «1С:Предприятии» могут быть
следующих видов:
■■ функция, которая возвращает какое-либо значение;
■■ функция, которая не возвращает значения (процедура);
■■ таблица вида Выражение в реляционном источнике данных.
Функции могут использоваться во встроенном языке, языке запросов
и системе компоновки данных. Для функций можно указывать права доступа,
регламентирующие использование этих функций.
Если во внешнем источнике данных существуют функции, возвращающие
таблицы, то необходимо также определить таблицы внешнего источника
данных вида Выражение.
В конфигурации может быть описано меньшее количество функций
и процедур, чем есть в реальной базе данных.
Также можно написать некоторую функцию (на языке используемой СУБД),
которая отсутствует в реальной базе данных. При этом надо учитывать следующие особенности:
■■ Если функция возвращает значение, то при использовании во встроенном
языке ее выражение, указанное в свойстве Выражение в источнике данных,
подставляется в конструкцию select . А при использовании в языке
запросов (и системе компоновки данных) происходит замена вызова
функции на собственно выражение.
■■ Если функция не возвращает значение, то не рекомендуется использование произвольного выражения для такой функции с целью
максимальной совместимости с различными СУБД. Использование таких
функций не поддерживается в языке запросов и системе компоновки
данных.

184

Технологии интеграции «1С:Предприятия 8.3»

ПОДРОБНЕЕ
Более подробно работа с функциями внешнего источника данных описана
в документации «1С:Предприятия» в разделе «Глава 24. Внешние
источники данных – Работа с процедурами и функциями внешних
источников данных».

Редактирование функций
Создание функций внешнего источника данных возможно как в ручном
режиме, так и при загрузке списка функций с помощью специального
конструктора.
Чтобы загрузить список функций из внешней базы данных, нужно в окне
редактирования свойств внешнего источника на закладке Функции нажать
кнопку Добавить и выбрать пункт Выбрать из списка функций внешнего источника данных в конструкторе функций внешнего источника данных (рис. 2.7).

Рис. 2.7. Загрузка списка функций из внешнего источника данных

Затем нужно указать строку соединения с внешней базой данных, воспользовавшись окном Подключение к источнику данных (см. рис. 2.4).
После того как подключение к внешней базе успешно выполнено, будет
открыт список с перечнем процедур и функций подключенного источника
данных. Из этого списка можно выбрать необходимые функции, на основе
которых платформа создаст соответствующую структуру объектов, описывающих текущий источник данных (рис. 2.8).

Глава 2. Внешние источники данных

185

Рис. 2.8. Конструктор функций внешнего источника данных

В конструкторе можно указать, под каким именем функция будет создана
в конфигурации «1С:Предприятия».
После настройки указанных параметров, отметки нужных функций
и процедур и нажатия кнопки Готово произойдет загрузка функций внешнего
источника данных в конфигурацию.
ПОДРОБНЕЕ
Более подробно загрузка функций из внешнего источника данных будет
показана в разделе «Примеры использования».

Помимо этого список функций внешнего источника данных можно полностью заполнить вручную в конфигураторе. То есть функции можно создать
обычным образом, как подчиненные внешнему источнику объекты конфигурации, и установить их свойства.
При ручном заполнении списка функций особое внимание нужно обратить
на свойство Выражение в источнике данных. Значение этого свойства должно
содержать в себе выражение, которое будет использоваться в реальном
запросе к базе данных при использовании функции. В данном свойстве
можно не только указать обращение к реальной функции или сохраненной
процедуре базы данных, но и написать выражение на языке СУБД внешнего
источника данных.
Если с помощью функции выполняется обращение к реальной функции базы
данных, то значение в этом свойстве должно в точности соответствовать
имени и числу параметров исходной функции.
Можно также добавить функцию внешнего источника данных, которая
отсутствует в реальной базе данных. Эта функция должна быть написана

186

Технологии интеграции «1С:Предприятия 8.3»

на языке используемой СУБД. При этом рекомендуется выражение, помещенное
в свойство Выражение в источнике данных, заключать в круглые скобки, чтобы
избежать различных побочных эффектов во время исполнения реального
запроса.
Если свойство Возвращает значение установлено в значение Истина, то
в этом случае также необходимо указать, какого типа значение будет возвращаться данной функцией или выражением.
Если функция принимает параметры, то формальные параметры функций
внешнего источника данных описываются как &n, где n – номер параметра. Нумерация параметров начинается с 1. Например, функция,
принимающая два параметра, описывается следующим образом:
dbo.uspGetWhereUsedProductID(&1, &2).

Управление внешними источниками данных
В режиме «1С:Предприятие» при выполнении любой операции, в ходе
которой необходимо получать данные из внешнего источника данных, происходит попытка подключения к этому источнику, если подключение еще
не было выполнено.
Если подключение не выполнено, в клиентском приложении возникает
исключение. В этом случае пользователю предлагается диалог, в котором
можно уточнить параметры подключения и повторить подключение. Если
подключение выполнено успешно, то пользователю будет предложено повторить действие, во время которого произошла ошибка подключения.
Также имеется возможность заранее выполнить подключение вручную.
Для этого предназначена стандартная функция Управление внешними источниками данных, которая вызывается из меню Все функции > Стандартные функции
(рис. 2.9).

Рис. 2.9. Управление внешними источниками данных

Глава 2. Внешние источники данных

187

Пользователю, открывшему эту обработку, будут доступны все внешние
источники данных, созданные в конфигураторе, на которые он имеет права.
Колонка Подключен указывает состояние подключения к источнику данных
в этом сеансе.
С помощью группы команд Администрирование можно указать общие параметры подключения (команда Изменить общие параметры…) и параметры
подключения конкретных пользователей в том случае, если эти параметры
отличаются от общих параметров (команда Изменить параметры пользователей…) – рис. 2.10.

Рис. 2.10. Параметры соединения

Флажок перед именем параметра означает, что данный параметр используется в данном наборе параметров соединения.
Нажатие кнопки Подключиться в обработке Управление внешними источниками
данных (см. рис. 2.9) открывает диалог, в котором можно задать (или уточнить) параметры подключения к выбранному внешнему источнику данных
(рис. 2.11).

Рис. 2.11. Параметры подключения

188

Технологии интеграции «1С:Предприятия 8.3»

Необходимость указания пользователя и пароля в данном диалоге зависит
от драйвера ODBC и содержания строки соединения. Возможны ситуации,
когда ввод логина и пароля не требуется.
Если пользователь не является администратором внешнего источника
данных, то кнопки Общие параметры… и Параметры пользователя… для него
недоступны. Флажок Использовать аутентификацию операционной системы
доступен только в том случае, если у пользователя имеется право ИзменениеАутентификацииОССеанса.
После нажатия кнопки Подключиться происходит подключение к внешнему
источнику данных. В случае успешного подключения изменяется содержимое колонки Подключен списка внешних источников данных.

Примеры использования
В этом разделе мы рассмотрим различные примеры использования
и модификации данных во внешних источниках из прикладного решения
«1С:Предприятия». В качестве внешних источников данных будут выступать
реляционные схемы (базы данных), созданные с помощью СУБД MySQL.
Подключение к этим базам данных будет выполняться как с помощью полной
строки соединения, так и с помощью заранее созданного DSN.
Сначала мы реализуем настройку внешних источников данных в конфигураторе, затем покажем подключение к ним в режиме 1С:Предприятие
и отображение, а также возможность интерактивного редактирования
данных, полученных из внешних источников.
После этого рассмотрим, как программно заполнить данные прикладного решения (например, справочник на основе данных, содержащихся во
внешней таблице), а при добавлении или изменении записей справочника
покажем, как синхронизировать эту информацию в «1С:Предприятии»
с данными внешней таблицы.
Также мы рассмотрим работу с функциями внешнего источника, одну из
которых мы создадим самостоятельно на языке MySQL.
Кроме того, покажем пример использования данных из внешних источников для работы стандартных учетных механизмов в прикладном решении
«1С:Предприятия».
В заключение продемонстрируем небольшой отчет, построенный с помощью
системы компоновки данных, показывающий информацию из внешнего
источника.

Глава 2. Внешние источники данных

189

Исходная информация для примеров
Как уже говорилось, в качестве внешних источников данных для наших
примеров мы будем использовать схемы, созданные с помощью СУБД
MySQL. Эта СУБД предварительно установлена на локальный компьютер,
на котором будут демонстрироваться примеры. Вместе с ней также установлена оболочка MySQL Workbench, с помощью которой мы создадим внешние
схемы, таблицы и функции.
Итак, сначала создадим схему goods. В этой схеме создадим таблицу goods
с ключевым полем id типа int(10) и полем description типа varchar(45).
У поля ключа включены свойства Primary Key и Not Null. Это значит,
что поле id – это поле первичного ключа, и в этом поле не может содержаться
значение Null. В поле description, наоборот, по умолчанию записывается
Null.
Добавим в эту таблицу несколько записей. Структура таблицы goods и содержащаяся в ней информация показаны на рис. 2.12.

Рис. 2.12. Структура и данные таблицы «goods»

190

Технологии интеграции «1С:Предприятия 8.3»

Затем создадим схему spa. В этой схеме создадим таблицу clients с ключевым
полем id типа int(10) и полем name типа varchar(30). У поля ключа включены свойства Primary Key, Not Null, Unsigned, Auto Increment.
Это значит, что поле id – это поле первичного ключа, самовозрастающее,
беззнаковое и в этом поле не может содержаться значение Null. В поле name,
наоборот, по умолчанию записывается Null.
Добавим в эту таблицу несколько записей. Структура таблицы clients и содержащаяся в ней информация показаны на рис. 2.13.

Рис. 2.13. Структура и данные таблицы «clients»

В схеме spa создадим еще одну таблицу spa_treatments с ключевым полем id
типа int(10) и полем description типа varchar(45). У поля ключа включены свойства Primary Key, Not Null, Unsigned, Auto Increment.
Это значит, что поле id – это поле первичного ключа, самовозрастающее,
беззнаковое и в этом поле не может содержаться значение Null. В поле
description, наоборот, по умолчанию записывается Null.

Глава 2. Внешние источники данных

191

Добавим в эту таблицу несколько записей. Структура таблицы spa_treatments
и содержащаяся в ней информация показаны на рис. 2.14.

Рис. 2.14. Структура и данные таблицы «spa_treatments»

В схеме spa создадим еще одну таблицу visits. У этой таблицы будут два
ключевых поля: client_id типа int(11) и spa_treatment_id типа int(11)
и еще одно поле number типа int(11). У ключевых полей включены свойства
Primary Key, Not Null. Это значит, что поля client_id и spa_treatment_id –
это поля первичного ключа и в этих полях не может содержаться значение
Null. В поле number, наоборот, по умолчанию записывается Null.
Добавим в эту таблицу несколько записей. Структура таблицы visits и содержащаяся в ней информация показаны на рис. 2.15.

192

Технологии интеграции «1С:Предприятия 8.3»

Рис. 2.15. Структура и данные таблицы «visits»

Теперь нам нужно решить, каким образом мы будем соединяться
с этими схемами, чтобы получать из них данные в прикладном решении
«1С:Предприятия». Мы покажем оба способа: со схемой spa будем соединяться напрямую, с помощью полной строки соединения, а со схемой goods
будем соединяться с помощью заранее созданного DSN, о котором речь
пойдет в следующем разделе.

DSN
Итак, создадим описание источника данных DSN (Data Source Name),
с помощью которого мы будем соединяться со схемой goods.
Для этого мы будем использовать 64-разрядную версию утилиты администрирования ODBC, которая на нашем компьютере (с 64-разрядной версией
ОС Windows) расположена в каталоге C:\Windows\System32\odbcad32.
exe. При этом версия клиентского приложения «1С:Предприятия», которое

Глава 2. Внешние источники данных

193

будет отправлять запросы к внешнему источнику данных, также должна быть
64-разрядной. В нашем случае мы просто будем запускать «1С:Предприятие»
из конфигуратора для 64-разрядной версии ОС Windows.
Для установки соединения нам понадобится также драйвер ODBC для
MySQL, который был установлен вместе с самой СУБД. А также мы будем
соединяться с помощью веб-сервера, установленного на нашем локальном
компьютере («localhost»).
Итак, запустим утилиту администрирования ODBC, перейдем на закладку
Системный DSN, нажмем кнопку Добавить… и в списке драйверов выберем
MySQL ODBC 8.0 Unicode Driver.
В открывшемся окне зададим произвольное имя и описание источника
данных (в нашем случае Goods), в качестве имени сервера укажем localhost,
а в качестве имени и пароля пользователя укажем те данные, которые мы
задавали при установке СУБД MySQL. В поле Database укажем имя схемы,
с которой требуется установить соединение, – goods (рис. 2.16).

Рис. 2.16. Настройка DSN

194

Технологии интеграции «1С:Предприятия 8.3»

В заключение, не покидая окно настройки DSN, можно нажать кнопку Test
и убедиться, что соединение с базой данных успешно устанавливается.
ПРИМЕЧАНИЕ
Мы показали пример создания системного DSN, доступного всем
пользователям данного компьютера. Точно так же можно создать
и пользовательский DSN, но надо понимать, что он будет доступен только
тому пользователю, который его настроил.

Работа с внешними источниками данных
в конфигураторе и в режиме «1С:Предприятие»
Итак, сначала создадим в конфигураторе объекты для отображения и модификации созданных ранее внешних данных. Для этого предназначены объекты
конфигурации ВнешниеИсточникиДанных, которые группируются
в ветви дерева конфигурации Внешние источники данных. Чтобы не путаться,
объекты конфигурации для работы с внешними данными мы будем называть
«внешними источниками данных», а собственно хранилища внешних данных
(в нашем случае – схемы MySQL) – «внешними схемами» или «внешними
таблицами».
Источник данных, подключенный через DSN
Добавим в конфигурацию внешний источник данных с именем Товары. Этот
источник будет соединяться со схемой goods с помощью ранее настроенного
DSN (см. предыдущий раздел).
Загрузим таблицы внешнего источника данных с помощью конструктора.
Для этого в окне редактирования свойств внешнего источника на закладке
Данные нажмем кнопку Добавить и выберем пункт Выбрать из списка таблиц
внешнего источника данных (рис. 2.17).
В открывшемся окне Подключение к источнику данных укажем строку соединения с внешней схемой:
DSN= Goods (регистр символов не имеет значения).

Затем выберем тип используемой СУБД – MySQL (рис. 2.18).
Флажок Использовать аутентификацию операционной системы установлен
автоматически. Имя пользователя и пароль, которые использовались при
установке СУБД MySQL, здесь можно не задавать, так как они уже указаны
в DSN.

Глава 2. Внешние источники данных

195

Рис. 2.17. Загрузка таблиц внешнего источника данных с помощью конструктора

Рис. 2.18. Параметры подключения к внешней схеме

После того как подключение к внешней схеме успешно выполнено,
в конструкторе таблиц внешнего источника данных будет открыт список
с перечнем таблиц и полей внешней схемы.
Как мы знаем, в базе данных goods содержится единственная таблица goods.
Платформа автоматически определила тип таблицы (объектные данные),
тип и длину ее полей, определила, что поле id – это поле ключа и включила
отметку Разрешить Null только для поля description (рис. 2.19).

196

Технологии интеграции «1С:Предприятия 8.3»

Рис. 2.19. Конструктор таблиц внешнего источника данных «Товары»

Все это нас полностью устраивает. Отметим к переносу во внешний источник
данных всю внешнюю таблицу и нажмем Готово. В результате таблица goods
будет добавлена в список таблиц внешнего источника данных Товары.
Открыв окно редактирования свойств этой таблицы, можно увидеть ее
свойства, установленные в конструкторе, и изменить их (рис. 2.20).

Рис. 2.20. Свойства таблицы «goods»

Глава 2. Внешние источники данных

197

Источник данных, подключенный
с помощью полной строки соединения
Теперь добавим в конфигурацию еще один внешний источник данных
с именем Спа. Этот источник будет соединяться с базой данных spa не через
DSN, а напрямую, с помощью полной строки соединения.
Загрузим таблицы внешнего источника данных с помощью конструктора.
Для этого в окне редактирования свойств этого объекта на закладке Данные
нажмем кнопку Добавить и выберем пункт Выбрать из списка таблиц внешнего
источника данных.
В открывшемся окне Подключение к источнику данных укажем строку соединения с внешней базой данных:
DRIVER=MySQL
ODBC
8.0
localhost;Database=spa

Unicode

Driver;Data

Source=

Как видно из строки соединения, в качестве драйвера ODBC (DRIVER)
здесь используется MySQL ODBC 8.0 Unicode Driver, в качестве сервера (Data
Source) – localhost, а соединение выполняется со схемой (Database) spa.
Затем выберем тип используемой СУБД – MySQL (рис. 2.21).

Рис. 2.21. Параметры подключения к внешней схеме

В этом варианте подключения требуется задать имя пользователя и пароль,
которые использовались при установке СУБД MySQL. Установим флажок
Использовать стандартную аутентификацию и введем имя и пароль пользователя в соответствующие поля под этим флажком.
После того как подключение к внешней схеме успешно выполнено,
в конструкторе таблиц внешнего источника данных будет открыт список
с перечнем таблиц и полей внешней схемы.

198

Технологии интеграции «1С:Предприятия 8.3»

Как мы знаем, в схеме spa содержится три таблицы: clients, spa_treatments,
visits. Платформа автоматически определила тип таблиц clients и spa_treatments
как объектные данные, а тип таблицы visits как необъектные данные, так как
у нее два ключевых поля. Также платформа определила тип и длину простых
полей таблиц, правильно установила ключевые поля и включила отметку
Разрешить Null в соответствии со свойствами полей в схеме spa (рис. 2.22).

Рис. 2.22. Конструктор таблиц внешнего источника данных «Спа»

Не откладывая на потом, прямо в конструкторе установим, что поле name
будет являться полем представления (свойство Поле представления) для
таблицы clients, а поле description будет являться полем представления для
таблицы spa_treatments.
Укажем, что ключевые поля таблицы visits будут ссылочными. То есть для
поля client_id укажем, что оно будет ссылаться на таблицу clients, а поле spa_
treatment_id – на таблицу spa_treatments.
Затем включим отметку у свойства Только чтение для поля client_id таблицы
clients, так как это ключевое поле является самовозрастающим (Auto
Increment), т. е. ключ будет автоматически увеличиваться на единицу при
добавлении новой записи в таблицу. Поэтому запись в это поле должна быть
запрещена. И также по аналогичным причинам включим отметку у свойства
Только чтение для поля spa_treatment_id таблицы spa_treatments.
Отметим к переносу во внешний источник данных все три внешние таблицы
и нажмем Готово. В результате таблицы clients, spa_treatments, visits будут
добавлены в список таблиц внешнего источника данных Спа.
Открыв окно редактирования свойств каждой таблицы, можно увидеть ее
свойства, установленные в конструкторе, и изменить их (рис. 2.23, 2.24).

Глава 2. Внешние источники данных

Рис. 2.23. Свойства таблицы «clients»

Рис. 2.24. Свойства таблицы «visits»

199

200

Технологии интеграции «1С:Предприятия 8.3»

Подключение и отображение внешних источников данных
в режиме «1С:Предприятие»
Теперь проверим, как все это работает. Как видите, мы ничего не программировали. Главное – все правильно настроить, и все «заработает само».
Запустим «1С:Предприятие». В панели функций основного раздела мы видим
команды для открытия форм списков таблиц (clients, goods, spa_treatments,
visits), которые содержатся во внешних источниках данных (Товары и Спа),
описанных в нашей конфигурации.
Нажмем на команду goods для открытия списка товаров. Напомним, что эта
таблица принадлежит источнику данных Товары, который подключается
к прикладному решению с помощью заранее созданного DSN. Поскольку
подключение к этому источнику данных в режиме 1С:Предприятие еще ни
разу не выполнялось, откроется окно подключения к источнику данных
Товары.
Надо понимать, что, хотя мы уже подключались к этому источнику данных
в конфигураторе, это лишь частный случай, когда параметры соединения
в режиме «1С:Предприятия» и в конфигураторе совпадают. На самом деле
в каждом клиентском приложении настройку соединения нужно выполнять
заново. Но делать это нужно только один раз. Впоследствии параметры
соединения с источником данных запоминаются и соединение происходит
автоматически.
Нажмем на кнопку Изменить общие параметры… и зададим такие же параметры соединения с этим источником данных, как и в конфигураторе
(рис. 2.25).
После нажатия на кнопку Подключиться соединение устанавливается и платформа предлагает повторить действие. Обновим данные в списке товаров
клавишей F5. В результате мы увидим содержимое таблицы goods, которую
мы заполнили в оболочке СУБД MySQL (рис. 2.26).
Обратите внимание, что, хотя мы не создавали никаких форм для наших
таблиц, платформа автоматически генерирует для них формы списков,
объектов и т. п. Вообще же для таблиц в конфигураторе можно разработать
различные собственные формы, которые будут использоваться в прикладном
решении. То есть работа с формами таблиц внешнего источника данных
аналогична работе с формами других прикладных объектов.

Глава 2. Внешние источники данных

Рис. 2.25. Подключение к источнику данных «Товары»

Рис. 2.26. Список товаров

201

202

Технологии интеграции «1С:Предприятия 8.3»

Таким образом, содержимое внешних данных сразу после подключения, без
всяких дополнительных усилий, отображается в интерфейсе прикладного
решения. Это очень удобно, так как позволяет быстро посмотреть содержимое таблиц и понять, где находятся нужные данные.
Кроме того, разработчик может интерактивно добавить запись во внешний
источник, изменить запись в нем или совсем ее удалить. Это тоже может
быть очень полезной возможностью для отладки различных алгоритмов
обмена данными, получения или импорта внешних данных. Например, если
разработчику нужны какие-то элементарные тестовые данные во внешнем
источнике, он может ввести их вручную в режиме 1С:Предприятие, ничего
при этом не программируя.
Добавим новый товар (с наименованием «Бальзам») в таблицу goods нашего
прикладного решения. В результате после обновления данных мы увидим его
и в исходной таблице в оболочке СУБД MySQL (рис. 2.27).

Рис. 2.27. Интерактивное изменение данных во внешней таблице

Необходимо помнить, что во внешних таблицах выполняется только непосредственное удаление записей.
И в обратную сторону все также работает – при добавлении записи
в таблицу goods в оболочке СУБД MySQL мы увидим ее в списке товаров
в «1С:Предприятии».

Глава 2. Внешние источники данных

203

Однако возможность интерактивного редактирования, конечно же,
не является основной целью использования внешних источников данных
в прикладном решении. Как будет показано в следующем разделе, наиболее
актуальна задача синхронизации данных между прикладным решением
и внешней базой данных.
Теперь из меню Все функции откроем стандартную обработку Управление
внешними источниками данных. Мы видим здесь список наших источников
данных, причем источник данных Спа еще не подключен. Можно нажать на
кнопку Подключиться и соединиться с источником прямо сейчас (рис. 2.28).

Рис. 2.28. Стандартная обработка «Управление внешними источниками данных»

Выделим внешний источник данных Спа, в открывшемся окне Подключение
к источнику данных нажмем на кнопку Изменить общие параметры… и зададим
такие же параметры соединения с этим источником данных, как и в конфигураторе. После возврата в окно Подключение к источнику данных нужно
продублировать еще раз имя и пароль пользователя (рис. 2.29).
После нажатия на кнопку Подключиться соединение устанавливается.
Но можно и не соединяться с источником данных заранее, поскольку при
первом обращении к любой из таблиц источника данных подключение будет
происходить автоматически с учетом тех параметров соединения, которые
мы указали ранее в режиме 1С:Предприятие.

204

Технологии интеграции «1С:Предприятия 8.3»

Рис. 2.29. Подключение к источнику данных «Спа»

В заключение посмотрим, как отображается в интерфейсе необъектная
таблица visits. Напомним, что у этой таблицы два ссылочных поля: поле client_
id ссылается на таблицу clients и поле spa_treatment_id ссылается на таблицу
spa_treatments. А у этих двух объектных таблиц (clients, spa_treatments)
полями представления являются соответственно name и description. Поэтому
в таблице visits мы видим вместо ключевых полей фамилии клиентов и наименования косметических процедур, которые были им выполнены (рис. 2.30).

Глава 2. Внешние источники данных

205

Рис. 2.30. Таблица «visits»

Программная синхронизация
В этом разделе мы рассмотрим примеры синхронизации данных между структурами хранения информации «1С:Предприятия» и внешними данными.
При работе с внешними источниками данных из встроенного языка есть
возможность не только получать, но и записывать данные в эти источники.
Ниже мы рассмотрим примеры заполнения справочника внешними данными,
и, наоборот, при добавлении или изменении данных в прикладном решении
эти же данные будут записываться и во внешнюю таблицу.
Таким образом, с помощью внешних источников данных могут решаться
различные задачи интеграции прикладных решений «1С:Предприятия»
с другими информационными системами.
Предположим, в нашей конфигурации есть справочник Товары, который
должен программно пополняться новыми товарами, которые появляются
в таблице goods внешней схемы goods. А при добавлении записей в справочник товаров нужно записывать их и во внешнюю таблицу.
Для решения этой задачи нам нужно добавить в конфигурацию две общие
команды: ЗагрузитьТоварыИзВнешнегоИсточника и ВыгрузитьТоварыВоВнешнийИсточник. Первая команда будет читать новые товары, если они появились
во внешней таблице, и записывать их в справочник. Вторая команда будет
записывать новые товары, если они появились в справочнике, во внешнюю
таблицу.

206

Технологии интеграции «1С:Предприятия 8.3»

На самом деле алгоритм программной синхронизации данных может
вызываться, например, регламентным заданием, которое запускается
по расписанию, но мы для упрощения примера пока используем команды.
Связь со схемой goods устанавливается через внешний источник данных
Товары, который мы описали в разделе «Источник данных, подключенный
через DSN». Список товаров отображается в таблице goods этого источника
данных.
Итак, добавим в нашу конфигурацию общую команду ЗагрузитьТоварыИзВнешнегоИсточника. Из обработчика этой команды будет вызываться
процедура ЗагрузитьТоварыИзВнешнегоИсточника(). Заполним ее
следующим образом (листинг 2.1).
Листинг 2.1. Процедура «ЗагрузитьТоварыИзВнешнегоИсточника»
&НаСервере
Процедура ЗагрузитьТоварыИзВнешнегоИсточника()
// Создать менеджер справочника Товары.
Товары = Справочники.Товары;

// Получить список товаров с помощью запроса к таблице goods внешнего источника данных Товары.
Запрос = Новый Запрос;
Запрос.Текст =

″ВЫБРАТЬ

|
Товары.id КАК Товар,

|
Товары.description КАК Наименование

|ИЗ

|
ВнешнийИсточникДанных.Товары.Таблица.goods КАК Товары
|

|УПОРЯДОЧИТЬ ПО

|
Товар″;
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
// Добавить товары из таблицы goods в справочник Товары, если их там еще нет.
Пока Выборка.Следующий() Цикл

ТоварСсылка = Товары.НайтиПоНаименованию(Выборка.Наименование, Истина);

Если ТоварСсылка = Товары.ПустаяСсылка() ИЛИ ТоварСсылка = Неопределено Тогда

Товар = Товары.СоздатьЭлемент();

Иначе

Товар = ТоварСсылка.ПолучитьОбъект();

КонецЕсли;


Товар.Наименование = Выборка.Наименование;

Товар.Записать();
КонецЦикла;
КонецПроцедуры

Глава 2. Внешние источники данных

207

В этой процедуре мы сначала создаем менеджер для обращения к справочнику Товары. Затем с помощью запроса к таблице goods внешнего источника
данных Товары мы получаем список товаров, находящихся во внешней
таблице. Таблица внешнего источника данных описывается в языке запросов
как ВнешнийИсточникДанных..Таблица.. Остальные конструкции в тексте запроса нам хорошо знакомы
и не требуют пояснений.
По мере обхода выборки из результата запроса мы добавляем отсутствующие
товары из таблицы goods в справочник Товары. Поиск товара в справочнике
происходит по полному соответствию наименования товара в справочнике
и во внешней таблице. Если же товар уже существует в справочнике, то от
найденной ссылки на товар получается объект и данные этого товара синхронизируются между внешней таблицей и справочником.
Проверим, как это работает. Запустим «1С:Предприятие» и из меню Сервис
выполним команду Загрузить товары из внешнего источника. В результате
в справочник Товары (который пока пуст) будут скопированы все товары
из таблицы goods внешнего источника данных (рис. 2.31).

Рис. 2.31. Программная синхронизация данных
между справочником и внешним источником

208

Технологии интеграции «1С:Предприятия 8.3»

Теперь создадим новый товар (с наименованием «Кондиционер») в списке
товаров внешнего источника данных. После этого выполним еще раз команду
Загрузить товары из внешнего источника. В результате новый товар появится
в справочнике Товары (рис. 2.32).

Рис. 2.32. Программная синхронизация данных
между справочником и внешним источником

ПРИМЕЧАНИЕ
Чтобы увидеть программно добавленные записи, нужно обновить данные
в списках клавишей F5.

Теперь реализуем синхронизацию данных в обратную сторону – от справочника к внешнему источнику.
Итак, добавим в нашу конфигурацию общую команду ВыгрузитьТоварыВоВнешнийИсточник. Из обработчика этой команды будет вызываться
процедура ВыгрузитьТоварыВоВнешнийИсточник(). Заполним ее следующим образом (листинг 2.2).

Глава 2. Внешние источники данных

209

Листинг 2.2. Процедура «ВыгрузитьТоварыВоВнешнийИсточник»
&НаСервере
Процедура ВыгрузитьТоварыВоВнешнийИсточник()
// Создать менеджер таблицы goods внешнего источника данных Товары.
Товары = ВнешниеИсточникиДанных.Товары.Таблицы.goods;
// Получить список товаров с помощью запроса к справочнику Товары.
Запрос = Новый Запрос;
Запрос.Текст =

″ВЫБРАТЬ

|
Товары.Код КАК Код,

|
Товары.Наименование КАК Наименование

|ИЗ

|
Справочник.Товары КАК Товары
|

|УПОРЯДОЧИТЬ ПО

|
Код″;
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
// Добавить товары из справочника Товары во внешнюю таблицу goods, если их там еще нет.
Пока Выборка.Следующий() Цикл

ТоварСсылка = Товары.НайтиПоПолю(″description″, Выборка.Наименование);

Если ТоварСсылка = Товары.ПустаяСсылка() ИЛИ ТоварСсылка = Неопределено Тогда

Товар = Товары.СоздатьОбъект();

Иначе

Товар = ТоварСсылка.ПолучитьОбъект();

КонецЕсли;


Товар.id = Число(Выборка.Код);

Товар.description = Выборка.Наименование;

Товар.Записать();
КонецЦикла;
КонецПроцедуры

В этой процедуре мы сначала создаем менеджер таблицы goods внешнего источника данных Товары (ВнешниеИсточникиДанных.Товары.
Таблицы.goods).

Затем с помощью запроса к справочнику Товары мы получаем список
товаров, который нужно синхронизировать со списком товаров во внешней
таблице.
По мере обхода выборки из результата запроса мы добавляем отсутствующие
товары из справочника Товары в таблицу goods. Поиск товара во внешней
таблице происходит методом менеджера таблицы НайтиПоПолю() по
полному соответствию наименования товара в таблице goods (поле description)
и в справочнике.

210

Технологии интеграции «1С:Предприятия 8.3»

Если такого товара в таблице еще нет, то он добавляется методом менеджера
таблицы СоздатьОбъект(). Если есть, то от найденной ссылки на товар
получается объект и данные этого товара синхронизируются между внешней
таблицей и справочником.
Запустим «1С:Предприятие», откроем справочник Товары и добавим новый
элемент. Из меню Сервис выполним команду Выгрузить товары во внешний
источник. В результате новый товар появится в таблице goods внешнего источника данных (рис. 2.33).

Рис. 2.33. Программная синхронизация данных
между справочником и внешним источником

Глава 2. Внешние источники данных

211

Работа с функциями
В этом разделе мы покажем примеры использования функций внешних
источников данных. Причем можно использовать как реально существующую во внешней базе данных функцию, так и написать собственную
функцию (на языке используемой СУБД), которая отсутствует в реальной
базе данных.
Для примера рассмотрим такую задачу: предположим, что во внешней
таблице существуют определенные правила, по которым создается ключ
новой записи. Эти правила описываются функцией (внешней или хранимой
в СУБД). Нам нужно обеспечить возможность ввода нового элемента
в прикладное решение «1С:Предприятия» и синхронного создания такого же
элемента во внешней таблице. Но при этом его ключ должен создаваться по
этим принятым правилам. Для простоты будем считать, что правило заключается в том, чтобы увеличить значение кода на единицу, но в реальности
это может быть более сложное правило, зависящее от алгоритмов внешнего
источника.
Реализуем эту задачу двумя способами: сначала самостоятельно создадим
нужную функцию во внешнем источнике данных в конфигураторе, а потом
импортируем ту функцию, которая существует во внешней схеме, и используем ее.
Для простоты вызывать эти функции будем так же, как и в предыдущем
разделе, с помощью общих команд. Показанные ниже примеры являются
просто демонстрационными и не имеют никакого прикладного значения.
Собственная функция
Предположим, при программном добавлении косметической процедуры
в таблицу spa_treatments внешнего источника данных нам нужно также
заполнять ключевое поле этой таблицы автоматически возрастающим
значением.
Для этого мы будем использовать собственную функцию, которая возвращает
максимальное значение ключевого поля в таблице spa_treatments схемы spa,
а затем увеличивать это значение на единицу и записывать в ключевое поле
таблицы.
Связь со схемой spa устанавливается через внешний источник данных Спа,
который мы описали в разделе «Источник данных, подключенный с помощью
полной строки соединения». Список косметических процедур отображается
в таблице spa_treatments этого источника данных.

212

Технологии интеграции «1С:Предприятия 8.3»

Откроем окно редактирования источника данных Спа, содержащего эту
таблицу, и на закладке Функции нажмем кнопку Добавить. В появившемся
окне конструктора функций внешнего источника данных выберем пункт
Вручную и нажмем Готово. Дадим функции имя ПолучитьМаксимальныйКлючТаблицы, укажем, что она возвращает значение типа Число (10, 0), а в поле
Выражение в источнике данных напишем выражение: MAX(id) FROM spa.
spa _ treatments (рис. 2.34).

Рис. 2.34. Добавление собственной функции внешнего источника данных

Надо понимать, что при использовании функции во встроенном языке
выражение, указанное в свойстве Выражение в источнике данных, будет
использоваться в реальном запросе к базе данных. Это выражение непосредственно подставляется в конструкцию select , поэтому слово «select»
писать уже не нужно.
Теперь добавим в нашу конфигурацию еще одну общую команду ДобавитьСпаПроцедуру. Обработчик команды заполним следующим образом (листинг
2.3).

Глава 2. Внешние источники данных

213

Листинг 2.3. Обработчик команды «ДобавитьСпаПроцедуру»
&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
Оповещение = Новый ОписаниеОповещения(″ПослеВводаСтроки″, ЭтотОбъект);
ПоказатьВводСтроки(Оповещение, ″Введите имя процедуры″);
КонецПроцедуры

Ввод наименования новой спа-процедуры мы будем выполнять с помощью
немодального метода ПоказатьВводСтроки(). В этот метод первым параметром мы передаем описание оповещения, указывающее на экспортную
процедуру ПослеВводаСтроки(), которая будет вызвана по окончании
ввода наименования спа-процедуры (листинг 2.4).
Листинг 2.4. Процедура «ПослеВводаСтроки»
&НаКлиенте
Процедура ПослеВводаСтроки(Строка, Параметры) Экспорт
Если НЕ Строка = Неопределено Тогда

ДобавитьПроцедуруВоВнешнийИсточник(Строка);
КонецЕсли;
КонецПроцедуры

В этом обработчике оповещения, в случае если строка с наименованием
спа-процедуры (содержащаяся в параметре Строка) задана, мы вызываем
процедуру ДобавитьПроцедуруВоВнешнийИсточник(), в которую передаем введенное наименование (листинг 2.5).
Листинг 2.5. Процедура «ДобавитьПроцедуруВоВнешнийИсточник»
&НаСервере
Процедура ДобавитьПроцедуруВоВнешнийИсточник(СтрокаПроцедуры)

// Получить значение ключа с помощью функции ПолучитьМаксимальныйКлючТаблицы()
// внешнего источника данных Спа.
ЗначениеКлюча = ВнешниеИсточникиДанных.Спа.ПолучитьМаксимальныйКлючТаблицы() + 1;
// Создать менеджер таблицы spa_treatments внешнего источника данных Спа.
Процедуры = ВнешниеИсточникиДанных.Спа.Таблицы.spa_treatments;
// Найти запись в таблице spa_treatments по наименованию добавляемой процедуры.
Ссылка = Процедуры.НайтиПоПолю(″description″, СтрокаПроцедуры);
// Добавить новую процедуру в таблицу spa_treatments, если ее там еще нет.
Если Ссылка = Процедуры.ПустаяСсылка() ИЛИ Ссылка = Неопределено Тогда

НоваяПроцедура = Процедуры.СоздатьОбъект();


214

Технологии интеграции «1С:Предприятия 8.3»


// Получить и установить изменяемыеполя таблицы.

ИзменяемыеПоля = НоваяПроцедура.ПолучитьИзменяемыеПоля();

Если ИзменяемыеПоля.Найти(″id″) = Неопределено Тогда

// Включить ключевое поле в состав изменяемых полей для новой записи.

ИзменяемыеПоля.Добавить(″id″);

НоваяПроцедура.УстановитьИзменяемыеПоля(ИзменяемыеПоля);

КонецЕсли;


// Записать значение ключа в ключевое поле.

НоваяПроцедура.id = ЗначениеКлюча;

НоваяПроцедура.description = СтрокаПроцедуры;

НоваяПроцедура.Записать();
КонецЕсли;
КонецПроцедуры

В этой процедуре сначала мы получаем максимальное значение ключевого
поля id в таблице spa_treatments с помощью функции ПолучитьМаксимальныйКлючТаблицы() внешнего источника данных Спа. И увеличиваем
это значение на единицу.
Затем создаем менеджер таблицы spa_treatments внешнего источника данных
Спа
(ВнешниеИсточникиДанных.Спа.Таблицы.spa _ treatments).
Затем методом этого менеджера НайтиПоПолю() ищем запись в таблице
spa_treatments, у которой в поле description содержится точное соответствие
наименованию введенной процедуры (СтрокаПроцедуры).
Если такой процедуры в таблице еще нет, то она добавляется методом менеджера таблицы СоздатьОбъект(). При создании новой процедуры наряду
с наименованием мы записываем получившееся новое значение ключа
в ключевое поле (НоваяПроцедура.id = ЗначениеКлюча).

Но сначала мы должны включить ключевое поле в состав изменяемых полей
для новой записи. Если этого не сделать, то при записи новой процедуры
будет получена ошибка (рис. 2.35).

Рис. 2.35. Ошибка при записи в ключевое поле таблицы «spa_treatments»

Так произошло потому, что для ключевого поля id в таблице spa_treatments
мы установили свойство Только чтение, так как это поле в исходной таблице
внешней схемы определено как самовозрастающее.

Глава 2. Внешние источники данных

215

Поэтому теперь, прежде чем записывать значение в это поле, нужно включить его в состав изменяемых полей таблицы. Для этого нужно получить
изменяемые поля методом ПолучитьИзменяемыеПоля() для новой записи
таблицы, добавить имя поля id в массив изменяемых полей и передать этот
массив в метод УстановитьИзменяемыеПоля().

Теперь все должно быть хорошо. Запустим «1С:Предприятие» и из меню
Сервис выполним команду Добавить спа процедуру. В появившемся окне
введем наименование новой процедуры и нажмем ОК. В результате введенная
процедура со следующим по порядку значением ключевого поля появится
в таблице spa_treatments внешнего источника данных (рис. 2.36).

Рис. 2.36. Добавление процедуры в таблицу внешнего источника данных

Функция внешней схемы
Теперь покажем, как использовать уже существующую во внешней схеме
функцию в прикладном решении «1С:Предприятия».
Предположим, в схеме spa существует функция get _ next _ key _
record(), которая возвращает ключ следующей записи из таблицы
spa_treatments.
Нам нужно, так же как и в предыдущем примере, программно добавить
косметическую процедуру во внешнюю таблицу и заполнить ее ключ
с помощью функции get _ next _ key _ record().

216

Технологии интеграции «1С:Предприятия 8.3»

Сначала загрузим функции внешнего источника данных Спа, к которому
относится таблица spa_treatments, с помощью конструктора. Для этого в окне
редактирования свойств этого объекта на закладке Функции нажмем кнопку
Добавить и выберем пункт Выбрать из списка функций внешнего источника
данных. Ранее (при загрузке списка таблиц) мы уже настраивали в конфигураторе соединение с этим источником данных. Поэтому в открывшемся окне
Подключение к источнику данных подтвердим те же параметры соединения
и нажмем ОК.
После установки соединения в конструкторе функций внешнего источника
данных будет открыт список функций внешней схемы spa. Отметим к переносу функцию spa.get_next_key_record() и нажмем Готово (рис. 2.37).

Рис. 2.37. Конструктор функций внешнего источника данных «Спа»

В результате функция с именем spa_get_next_key_record будет добавлена
в список функций внешнего источника данных Спа. Открыв палитру свойств
этой функции, можно увидеть ее свойства, установленные в конструкторе,
и изменить их.
Свойство Выражение в источнике данных платформа определила правильно –
spa.get _ next _ key _ record(). Укажем также, что функция
возвращает значение типа Число (10, 0) (рис. 2.38).

Теперь добавим в нашу конфигурацию еще одну общую команду ДобавитьКосметическуюПроцедуру. Обработчик команды заполним следующим образом
(листинг 2.6).

Глава 2. Внешние источники данных

217

Рис. 2.38. Свойства функции «spa_get_next_key_record»
Листинг 2.6. Обработчик команды «ДобавитьКосметическуюПроцедуру»
&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
Оповещение = Новый ОписаниеОповещения(″ПослеВводаСтроки″, ЭтотОбъект);
ПоказатьВводСтроки(Оповещение, ″Введите имя процедуры″);
КонецПроцедуры

Ввод наименования новой спа-процедуры мы будем выполнять с помощью
немодального метода ПоказатьВводСтроки(). В этот метод первым параметром мы передаем описание оповещения, указывающее на экспортную
процедуру ПослеВводаСтроки(), которая будет вызвана по окончании
ввода наименования спа-процедуры (листинг 2.7).

218

Технологии интеграции «1С:Предприятия 8.3»

Листинг 2.7. Процедура «ПослеВводаСтроки»
&НаКлиенте
Процедура ПослеВводаСтроки(Строка, Параметры) Экспорт
Если НЕ Строка = Неопределено Тогда

ДобавитьПроцедуруВоВнешнийИсточник(Строка);
КонецЕсли;
КонецПроцедуры

В этом обработчике оповещения, в случае если строка с наименованием
спа-процедуры (содержащаяся в параметре Строка) задана, мы вызываем
процедуру ДобавитьПроцедуруВоВнешнийИсточник(), в которую передаем введенное наименование (листинг 2.8).
Листинг 2.8. Процедура «ДобавитьПроцедуруВоВнешнийИсточник»
&НаСервере
Процедура ДобавитьПроцедуруВоВнешнийИсточник(СтрокаПроцедуры)

// Получить значение ключа с помощью функции spa_get_next_key_record()
// внешнего источника данных Спа.
ЗначениеКлюча = ВнешниеИсточникиДанных.Спа.spa_get_next_key_record();
// Создать менеджер таблицы spa_treatments внешнего источника данных Спа.
Процедуры = ВнешниеИсточникиДанных.Спа.Таблицы.spa_treatments;
// Найти запись в таблице spa_treatments по наименованию добавляемой процедуры.
Ссылка = Процедуры.НайтиПоПолю(″description″, СтрокаПроцедуры);
// Добавить новую процедуру в таблицу spa_treatments, если ее там еще нет.
Если Ссылка = Процедуры.ПустаяСсылка() ИЛИ Ссылка = Неопределено Тогда

НоваяПроцедура = Процедуры.СоздатьОбъект();


// Получить и установить изменяемые поля таблицы.

ИзменяемыеПоля = НоваяПроцедура.ПолучитьИзменяемыеПоля();

Если ИзменяемыеПоля.Найти(″id″) = Неопределено Тогда

// Включить ключевое поле в состав изменяемых полей для новой записи.

ИзменяемыеПоля.Добавить(″id″);

НоваяПроцедура.УстановитьИзменяемыеПоля(ИзменяемыеПоля);

КонецЕсли;


// Записать значение ключа в ключевое поле.

НоваяПроцедура.id = ЗначениеКлюча;

НоваяПроцедура.description = СтрокаПроцедуры;

НоваяПроцедура.Записать();
КонецЕсли;
КонецПроцедуры

Глава 2. Внешние источники данных

219

Прокомментируем только строку для получения значения ключа следующей
записи внешней таблицы spa_treatments, которая выделена жирным шрифтом,
так как все остальное объяснялось в предыдущем примере (см. листинг 2.5).
Значение ключа получается с помощью функции spa _ get _ next _
key _ record() внешнего источника данных Спа.

Проверим, как это работает. Запустим «1С:Предприятие» и из меню Сервис
выполним команду Добавить косметическую процедуру. В появившемся окне
введем наименование новой процедуры и нажмем ОК. В результате введенная
процедура со следующим по порядку значением ключевого поля появится
в таблице spa_treatments внешнего источника данных (рис. 2.39).

Рис. 2.39. Добавление процедуры в таблицу внешнего источника данных

220

Технологии интеграции «1С:Предприятия 8.3»

Прикладное использование данных из внешних источников
Учет
Кроме примеров, описанных выше, данные из внешних источников могут
использоваться также и в прикладных целях. Например, во время разработки
и тестирования каких-то новых механизмов учета разработчику требуется
подстраховать себя. То есть нужно «незаметно» для пользователя продублировать данные, введенные им во внешнюю таблицу, в какую-то структуру
хранения данных «1С:Предприятия», чтобы потом проанализировать
эти данные.
Например, чтобы не заставлять пользователя дважды вводить данные о посещении клиентами спа-салона и выполненных им процедурах, «приспособим»
для этого внешнюю необъектную таблицу visits внешнего источника данных
Спа. Дадим таблице синоним – Визиты, а ее полям client_id, spa_treatment_id
и number дадим соответственно синонимы Клиент, Спа-процедура и Количество. Для поля number установим также свойство Значение заполнения
в значение 1 (рис. 2.40).

Рис. 2.40. Свойства таблицы «visits»

Глава 2. Внешние источники данных

221

Ранее, при настройке таблицы visits в конфигураторе, мы установили, что
у этой таблицы два ссылочных поля: поле client_id ссылается на таблицу
clients и поле spa_treatment_id ссылается на таблицу spa_treatments. А у этих
двух объектных таблиц (clients, spa_treatments) полями представления являются соответственно name и description.
Запустим «1С:Предприятие». Таблица visits теперь имеет привычный вид.
Вместо ключевых полей пользователь увидит фамилии клиентов и наименования косметических процедур, которые были им выполнены. Кроме того,
у самой таблицы и у ее полей теперь вполне понятные названия, и у пользователя может возникнуть ощущение, что он работает с привычным регистром
сведений, например (рис. 2.41).

Рис. 2.41. Ввод данных о посещениях клиентами спа-салона
во внешнюю таблицу «visits»

На самом деле пользователь будет вводить данные непосредственно
во внешнюю таблицу, а мы добавим в нашу конфигурацию «служебный»
регистр сведений, которого не будет видеть пользователь. Но при интерактивной записи данных во внешнюю таблицу соответствующая информация
также будет программно сохраняться и в этом регистре сведений. На основе
этого регистра можно затем построить различные отчеты и проверить полученную информацию.

222

Технологии интеграции «1С:Предприятия 8.3»

Итак, добавим в конфигурацию периодический регистр сведений ВизитыВСпа
и снимем у него флажок Использовать стандартные команды. Создадим
у регистра два измерения: измерение Клиент, имеющее тип ссылки на таблицу
clients внешнего источника данных Спа, и измерение Процедура, имеющее тип
ссылки на таблицу spa_treatments. А также создадим ресурс Количество числового типа (рис. 2.42).

Рис. 2.42. Структура регистра «ВизитыВСпа»

Теперь создадим форму записи таблицы visits. Затем создадим обработчик
события формы ПриЗаписиНаСервере() и заполним его следующим
образом (листинг 2.9).
Листинг 2.9. Обработчик события формы «ПриЗаписиНаСервере»
&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Подготовить менеджер записи.
ЗаписьРегистра = РегистрыСведений.ВизитыВСпа.СоздатьМенеджерЗаписи();
// Установить ключевые поля менеджера записи.
ЗаписьРегистра.Период = ТекущаяДата();

Глава 2. Внешние источники данных

223

ЗаписьРегистра.Клиент = Запись.client_id;
ЗаписьРегистра.Процедура = Запись.spa_treatment_id;
ЗаписьРегистра.Количество = Запись.number;
ЗаписьРегистра.Записать();
КонецПроцедуры

В этом обработчике мы с помощью менеджера записи добавляем запись
в регистр сведений ВизитыВСпа. Поле Период заполняем текущей датой,
поле измерения Клиент заполняем ссылкой на клиента, поле измерения
Процедура – ссылкой на процедуру, а поле ресурса Количество – количеством
выполненных клиенту процедур.
В результате регистр сведений может иметь следующее содержимое
(рис. 2.43).

Рис. 2.43. Содержимое регистра сведений «ВизитыВСпа»

Поясним еще раз, что содержимое регистра, показанное на рис. 2.43, приведено просто для ознакомления. В интерфейсе приложения регистр не будет
виден.
На основе информации, содержащейся в регистре, можно сформировать,
например, следующий журнал посещений спа-салона (рис. 2.44).

224

Технологии интеграции «1С:Предприятия 8.3»

Рис. 2.44. Журнал посещений спа-салона

Отчет
С помощью системы компоновки данных можно разрабатывать отчеты, основанные на данных таблиц внешних источников данных, точно так же, как
и для других объектов конфигурации.
Продемонстрируем это на примере все той же таблицы visits внешнего
источника данных Спа. Добавим в конфигурацию новый отчет с именем
ВизитыКлиентов. Откроем конструктор схемы компоновки данных и добавим
новый набор данных – запрос. Откроем конструктор запроса.
В качестве источника данных для запроса выберем таблицу visits внешнего
источника данных Спа. В список полей перенесем все поля из этой таблицы
(рис. 2.45).

Рис. 2.45. Конструктор запроса

Глава 2. Внешние источники данных

225

Нажмем ОК и вернемся в схему компоновки данных. На закладке Ресурсы
перенесем все доступные ресурсы в список ресурсов отчета (рис. 2.46).

Рис. 2.46. Ресурсы отчета

Перейдем на закладку Настройки. Добавим в структуру отчета группировку
по полю client_id, а в нее вложим группировку без указания группировочного
поля – Детальные записи. Затем на закладке Выбранные поля выберем поля,
которые будут выводиться в отчет: поле spa_treatment_id и ресурс number
(рис. 2.47).

Рис. 2.47. Группировки и поля отчета

В результате в режиме 1С:Предприятие сформированный отчет примет следующий вид (рис. 2.48).

226

Технологии интеграции «1С:Предприятия 8.3»

Рис. 2.48. Результат отчета

Обратите внимание, что вместо ключевых полей таблицы visits в отчет
выводятся фамилии клиентов и наименования выполненных им процедур,
поскольку эти поля являются полями представления таблиц clients и spa_
treatments, на которые они ссылаются.

Глава 3.
Обмен данными

Механизмы обмена данными позволяют решать различные задачи интеграции прикладных решений «1С:Предприятия» как друг с другом, так
и с другими информационными системами.
Обмен данными в системе «1С:Предприятие» реализуется благодаря использованию ряда средств технологической платформы, которые разработчик
может применять как по отдельности, так и в различных комбинациях,
в зависимости от конкретной решаемой задачи. Такой подход позволяет
обеспечить гибкость механизмов обмена и их настраиваемость на решение
как можно большего круга задач.
В состав средств платформы, используемых для построения схем обмена
данными, входят:
■■ объекты конфигурации План обмена,
■■ базовые средства работы с XML,
■■ средства XML-сериализации.
При помощи этих средств могут быть реализованы два механизма обмена
данными:
■■ универсальный механизм обмена данными,
■■ механизм распределенных информационных баз.

228

Технологии интеграции «1С:Предприятия 8.3»

Универсальный механизм обмена данными позволяет создавать произвольные
распределенные системы и практически не накладывает никаких ограничений на структуру создаваемой системы. Можно как связывать в единое
целое базы «1С:Предприятия» с отличными друг от друга конфигурациями,
так и осуществлять обмен с принципиально отличными информационными
системами (базами данных).
Механизм распределенных информационных баз, напротив, предназначен для
обмена данными только с идентичными конфигурациями «1С:Предприятия
8» и жестко регламентирует структуру создаваемой системы.
Основные отличия механизма универсального обмена данными от механизма
распределенных информационных баз можно проиллюстрировать следующей таблицей (табл. 3.1).
Таблица 3.1. Сравнение механизмов обмена данными

В узлах плана обмена
находятся

Универсальный механизм
обмена данными

Произвольные базы данных,
в том числе информационные
базы «1С:Предприятия»
Конфигурации информа- Могут быть разные (примеционных баз
нительно к информационным
базам «1С:Предприятия»)
В сообщениях обмена
Только изменения данных
передаются
Направление передачи
Произвольное, между двумя
связанными узлами
Формат файлов обмена
Создание распределенной базы и
выполнение обмена

Структура распределенной системы

Механизм распределенных
информационных баз

Только информационные
базы «1С:Предприятия 8»
Только идентичные

Изменения данных, изменения конфигурации
Изменения данных –
произвольное, изменения
конфигурации – от главного
к подчиненному узлу
Произвольный, чаще всего XML XML
Требуется написание кода (опре- Может быть выполнено
деление порядка разрешения
исключительно интеракколлизий, стратегии распротивными средствами, без
странения данных, создания
кодирования (действуют
начальных выгрузок, решение
соглашения по умолчанию)
задачи синхронизации данных)
Произвольная. Может
Древовидная. Любой узел
отсутствовать понятие
(кроме корневого) имеет
«главный – подчиненный»
один главный
(отсутствовать иерархия)
и произвольное количество
подчиненных узлов

ПОДРОБНЕЕ
Более подробно оба механизма обмена данными описаны в документации
«1С:Предприятия» в разделе «Глава 15. Механизмы обмена данными».

Глава 3. Обмен данными

229

Перед тем как перейти к рассмотрению возможностей организации как
универсального обмена, так и создания распределенных информационных
баз «1С:Предприятия», рассмотрим функциональность такого объекта платформы, как План обмена.

Планы обмена
При организации постоянного обмена может возникнуть ряд задач:
■■ с кем будет производиться обмен (определение состава участников
обмена);
■■ какими данными будет производиться обмен (с одной стороны это
определение перечня типов объектов; с другой стороны – определение
«экземпляров»);
■■ определение регламента обмена (например, нумерация сообщений, адресация, процесс разрешения коллизий и т. п.).
Все эти задачи в той или иной мере могут решаться с использованием функциональности планов обмена. Рассмотрим данный объект более подробно.
Как объект конфигурации ПланОбмена характеризуется составом реквизитов, табличных частей (составом реквизитов табличных частей),
определенными для него формами, макетами. У узла плана обмена
существуют стандартные реквизиты Код, Наименование, ЭтотУзел т. п.
В конфигурации может быть определено любое количество планов обмена.
Элементами данных плана обмена являются узлы плана обмена, подобно
тому как элементами данных справочника являются элементы справочника.
Каждый из узлов плана обмена обозначает участника обмена данными по
данному плану обмена. Один из узлов (он является предопределенным) соответствует данной информационной базе, а остальные – другим участникам,
с которыми данная информационная база может обмениваться данными
(рис. 3.1).

Рис. 3.1. Узлы плана обмена

230

Технологии интеграции «1С:Предприятия 8.3»

Реквизиты и табличные части узла обмена могут использоваться для
указания специфических данных по участнику обмена. С их помощью
определяется порядок взаимодействия с данным участником, привязка его
к другим объектам базы. Например, может указываться, что данный узел
с точки зрения базы данных является «таким-то» складом (элементом
справочника Склады), файл выгрузки данному получателю необходимо
отправлять по «такому-то» FTP-адресу и т. п. (рис. 3.2).

Рис. 2.3. Пример узла плана обмена

Данные переносятся между узлами с помощью сообщений. Средства работы
с сообщениями образуют инфраструктуру сообщений. Каждое сообщение
относится к определенному плану обмена, имеет определенный узелотправитель и определенный узел-получатель. Сообщение не может быть
отправлено неизвестному узлу и не может быть принято от неизвестного
узла. Каждое сообщение имеет свой собственный целочисленный номер.
С точки зрения инфраструктуры сообщений у узла существуют два свойства:
■■ номер отправленного сообщения,
■■ номер принятого сообщения.
Для предопределенного узла эти свойства смысла не имеют (база данных
сама с собой данными не обменивается).
Служба регистрации изменений предназначена для регистрации изменений
данных, производимых «1С:Предприятием», чтобы при обмене данными
иметь возможность передавать не все данные, а только новые, измененные
и удаленные.
Настройка состава объектов, для которых включается регистрация изменений, производится в режиме Конфигуратор, на закладке Основные объекта
конфигурации ПланОбмена (необходимо нажать кнопку Состав) – рис. 3.3.

Глава 3. Обмен данными

231

Рис. 3.3. Состав плана обмена

Можно сказать, что объекты, для которых включается регистрация изменений, являются входными данными для службы регистрации изменений.
Задача этой службы состоит в том, чтобы, опираясь на данный перечень
объектов, отслеживать изменения объектов, их удаление и производить соответствующие записи в таблицах регистрации изменений объектов. При этом
отслеживаются ситуации повторного изменения (но об этом более подробно
в разделе, посвященном именно службе регистрации изменений).
Обобщив изложенный ранее материал, можно сказать, что планы обмена:
■■ определяют состав участников обмена (любой узел, кроме предопределенного, соответствует какому-либо участнику обмена);
■■ позволяют производить регистрацию изменений объектов (служба регистрации изменений – как объектов, так и самой конфигурации);
■■ реализуют инфраструктуру сообщений.

232

Технологии интеграции «1С:Предприятия 8.3»

Служба регистрации изменений
Довольно сложно разделить службу регистрации изменений и инфраструктуру сообщений: их функциональность тесно связана. Но все же постараемся
рассмотреть особенности их работы по отдельности. Начнем со службы регистрации изменений.
Целью регистрации изменений является получение списка измененных
элементов данных, которые должны быть переданы в очередном сообщении
тому или иному узлу, с которым производится обмен данными. При каждом
изменении данных должно быть зарегистрировано, что имеются изменения, которые предстоит передать во все узлы, с которыми поддерживается
обмен этими данными. При получении подтверждения приема сообщения,
в котором были отправлены изменения, записи регистрации изменений
должны быть удалены.
Регистрация изменений может выполняться для следующих элементов
данных:
■■ КонстантаМенеджерЗначения..
■■ Объекты базы данных:
□□ СправочникОбъект.;
□□ ДокументОбъект.;
□□ ПланСчетовОбъект.;
□□ ПланВидовХарактеристикОбъект.;
□□ ПланВидовРасчетаОбъект.;
□□ БизнесПроцессОбъект.;
□□ ЗадачаОбъект..
■■ Наборы записей:
□□ РегистрСведенийНаборЗаписей.;
□□ РегистрБухгалтерииНаборЗаписей.;
□□ РегистрНакопленияНаборЗаписей.;
□□ ПоследовательностьНаборЗаписей.;
□□ РегистрРасчетаНаборЗаписей.;
□□ ПерерасчетНаборЗаписей..
Для каждого из приведенных элементов данных ведется своя таблица регистрации изменений. Таблицы имеют разную структуру в зависимости от
того, для каких элементов данных регистрируются изменения, но все-таки
структуры таблиц подобны. В структуре можно выделить три составляющих:

Глава 3. Обмен данными

233

■■ ключ элемента данных, для которого регистрируются изменения;
■■ ссылка на узел, в который изменение должно быть передано;
■■ номер сообщения, в котором изменение передано в первый раз.
Следует отметить, что в конфигурации может быть определено несколько
планов обмена. Для каждого из них может включаться регистрация изменений какого-либо объекта. Так вот, вне зависимости от количества планов
обмена, в которых участвует какой-либо объект, таблица регистрации изменений у объекта одна (можно сказать, что поле, содержащее ссылку на узел
плана обмена, может иметь составной тип).
Структуры таблиц регистрации изменений для разных данных отличаются
ключом, так как ключи у разных данных разные.
■■ Для константы ключом является идентификатор константы.
■■ Для объектов базы данных в качестве ключа используется ссылка
на объект.
■■ Для наборов записей, для которых определен регистратор, в качестве
ключа используется ссылка на объект-регистратор.
■■ Для набора записей регистра сведений, если регистратор не определен,
в качестве ключа используется совокупность измерений, входящих
в основной отбор. А если регистр сведений является периодическим
и включен основной отбор по периоду, то в ключ входит еще и период.
Основной отбор (для регистров сведений) позволяет определять логическую
единицу обмена данными. Рассмотрим это понятие более подробно (рис. 3.4).

Рис. 3.4. Свойство «Основной отбор»

Понятно, что физически минимальной единицей обмена является запись
регистра сведений. Но всегда ли правильно обмениваться записями?
Рассмотрим пример. Есть регистр сведений, в котором содержатся данные

234

Технологии интеграции «1С:Предприятия 8.3»

о дополнительных свойствах номенклатурных позиций. Речь идет именно
о свойствах, которые четко характеризуют товарную позицию. Если для
какого-то товара свойство имеет другое значение, то, значит, это другой
товар. Например, такое свойство, как количество компрессоров холодильника. У данного регистра сведений два измерения: ссылка на номенклатуру
и ссылка на свойство.
Если обмен будет производиться по каждой записи отдельно, у одного участника обмена добавили одно свойство, а у другого – другое, то в результате
обмена у элемента номенклатуры появятся два свойства. Но в данном случае
более правильно, чтобы свойства отдельно взятого товара представляли
собой единое логическое целое (входили в некий логический квант данных).
Как раз для того, чтобы объединить несколько физических записей в один
логический набор данных, и используется понятие Основной отбор. Если
в приведенном примере для измерения Номенклатура (содержащего ссылку
на номенклатурную позицию) установить свойство Основной отбор,
то при изменении хотя бы одной физической записи обмен будет производиться всеми свойствами данной номенклатуры.
В качестве примера рассмотрим следующие данные (наполнение регистра
сведений) – табл. 3.2.
Таблица 3.2. Записи регистра сведений
Измерение «Номенклатура»
(входит в основной отбор)

Измерение «Свойство»
(не входит в основной отбор)

Значение

Атлант МХМ 1704-00

Количество компрессоров

2

Атлант МХМ 1704-00

Цвет

Белый

Ardo TL 1000 EX-1

Тип загрузки

Вертикальная

Ardo TL 1000 EX-1

Количество подшипников

1

Добавим в регистр запись о новом свойстве (выделена серым фоном) –
табл. 3.3.
Таблица 3.3. В регистр сведений добавлена запись
Измерение «Номенклатура»
(входит в основной отбор)

Измерение «Свойство»
(не входит в основной отбор)

Значение

Атлант МХМ 1704-00

Количество компрессоров

2

Атлант МХМ 1704-00

Цвет

Белый

Атлант МХМ 1704-00

Отделов в морозильной камере

3

Ardo TL 1000 EX-1

Тип загрузки

Вертикальная

Ardo TL 1000 EX-1

Количество подшипников

1

Глава 3. Обмен данными

235

В результате будут зарегистрированы следующие изменения (табл. 3.4).
Таблица 3.4. Записи, попавшие в регистрацию изменений
Измерение «Номенклатура»
(входит в основной отбор)

Измерение «Свойство»
(не входит в основной отбор)

Значение

Атлант МХМ 1704-00

Количество компрессоров

2

Атлант МХМ 1704-00

Цвет

Белый

Атлант МХМ 1704-00

Отделов в морозильной камере

3

Если в основной отбор включить все измерения (если регистр сведений периодический, то включить в основной отбор и период), то логической единицей
обмена будет отдельно взятая запись регистра сведений.
Следует отметить тот факт, что при записи набора в регистр сведений состав
этого набора определяется логикой работы конфигурации и не обязательно
совпадает с набором, получаемым «при использовании» основного набора.
А при принудительной регистрации изменений для набора записей обязательно должен быть установлен отбор, совпадающий с основным (иначе
будет вызвано исключение). Такой порядок работы определяется тем, что
основной отбор является специфической чертой именно механизма обмена
данными. Из сказанного выше следует, что при изменении основного отбора
необходимо будет менять и программный код, в котором производится принудительная регистрация записей регистра сведений (во всех вхождениях
в конфигурацию).
Изменение элемента данных должно быть зарегистрировано для всех узлов,
в которые изменение должно быть передано. Таким образом, в результате
изменения элемента данных в таблице регистрации изменений должно
появиться N записей (наборов записей), где N – количество узлов, для которых
регистрируются изменения (все узлы плана обмена, за исключением предопределенного). В каждой из этих записей (наборе записей) указано одно и то
же значение ключа элемента данных и различные значения ссылки на узел.
Непосредственно после выполнения регистрации изменения номер сообщения имеет значение NULL. При первой отправке изменения в составе
сообщения в данное поле помещается номер сообщения, в котором изменение отправлено.
Рассмотрим приведенный алгоритм на примере. Считаем, что в плане обмена
включена регистрация для документа РасходнаяНакладная. В плане обмена
определено три узла: один – предопределенный, другие – с кодами Оптовый
и Розничный. После включения регистрации было изменено два документа.
В соответствии с этим таблица регистрации изменения примет следующий
вид (табл. 3.5).

236

Технологии интеграции «1С:Предприятия 8.3»

Таблица 3.5. Состав таблицы регистрации изменений
ДокументСсылка.РасходнаяНакладная

Узел

Номер сообщения

Расходная накладная № 1 от…

Оптовый

Null

Расходная накладная № 1 от…

Розничный

Null

Расходная накладная № 2 от…

Оптовый

Null

Расходная накладная № 2 от…

Розничный

Null

После этого были произведены формирование и отправка сообщения
с номером 1 для узла Оптовый. Таблица приняла следующий вид (табл. 3.6).
Таблица 3.6. Состав таблицы регистрации изменений
ДокументСсылка.РасходнаяНакладная

Узел

Номер сообщения

Расходная накладная № 1 от…

Оптовый

1

Расходная накладная № 1 от…

Розничный

Null

Расходная накладная № 2 от…

Оптовый

1

Расходная накладная № 2 от…

Розничный

Null

Далее было зарегистрировано изменение еще одного документа (табл. 3.7).
Таблица 3.7. Состав таблицы регистрации изменений
ДокументСсылка.РасходнаяНакладная

Узел

Номер сообщения

Расходная накладная № 1 от…

Оптовый

1

Расходная накладная № 1 от…

Розничный

Null

Расходная накладная № 2 от…

Оптовый

1

Расходная накладная № 2 от…

Розничный

Null

Расходная накладная № 3 от…

Оптовый

Null

Расходная накладная № 3 от…

Розничный

Null

После этого были сформированы и отправлены сообщения для узлов
Оптовый и Розничный. Фактически были проведены две выгрузки (сформировано два сообщения). Для узла Розничный сообщение имело номер 1, для узла
Оптовый – номер 2 (табл. 3.8).

Глава 3. Обмен данными

237

Таблица 3.8. Состав таблицы регистрации изменений
ДокументСсылка.РасходнаяНакладная

Узел

Номер сообщения

Расходная накладная № 1 от…

Оптовый

1

Расходная накладная № 1 от…

Розничный

1

Расходная накладная № 2 от…

Оптовый

1

Расходная накладная № 2 от…

Розничный

1

Расходная накладная № 3 от…

Оптовый

2

Расходная накладная № 3 от…

Розничный

1

После выгрузки для узла Оптовый запись в таблице для документа Расходная
накладная № 3 отмечается номером сообщения 2 (первый раз изменение было
передано именно в сообщении с таким номером), другие записи (для которых
в поле НомерСообщения содержалось значение 1) остались без изменений
(именно таким образом работает служба регистрации изменений).
Для узла Розничный все изменения были отнесены сообщением с номером 1.
Предположим, что после выполнения вышеуказанных действий произошло
повторное изменение документа Расходная накладная № 1. В этом случае
таблица регистрации изменений будет иметь следующий вид (табл. 3.9).
Таблица 3.9. Состав таблицы регистрации изменений
ДокументСсылка.РасходнаяНакладная

Узел

Номер сообщения

Расходная накладная № 1 от…

Оптовый

Null

Расходная накладная № 1 от…

Розничный

Null

Расходная накладная № 2 от…

Оптовый

1

Расходная накладная № 2 от…

Розничный

1

Расходная накладная № 3 от…

Оптовый

2

Расходная накладная № 3 от…

Розничный

1

Продолжая разговор о службе регистрации изменений, следует отметить, что
состав плана обмена распространяется на все узлы данного плана обмена.
Поэтому если, например, с разными узлами нужно обмениваться разным
составом информации, то необходимо создавать несколько планов обмена,
в которых объединять узлы, обменивающиеся одинаковым составом информации.
Например, если с одним узлом необходимо обмениваться изменениями
в справочнике Номенклатура, а с другим изменениями – в документах РасходнаяНакладная, то не стоит создавать один план обмена, лучше создать план

238

Технологии интеграции «1С:Предприятия 8.3»

обмена ПоНоменклатуре (с соответствующей настройкой состава данных,
по которым ведется регистрация изменений) и план обмена Расходные
(с другим составом).
Управление регистрацией изменений
При рассмотрении вышеприведенного примера акцент делался на заполнение
таблицы регистрации изменений. Каким образом производилась регистрация,
не учитывалось. Можно сказать, что рассматривалась функциональность
службы регистрации изменений с учетом того, что для всех объектов включена авторегистрация изменений. Пришло время рассмотреть и этот аспект
работы программного комплекса.
При определении состава объектов, для которых производится регистрация
изменений (кнопка Состав на закладке Основные объекта конфигурации План
обмена), для каждого объекта можно определить свойство Авторегистрация (рис. 3.5).

Рис. 3.5. Свойство «Авторегистрация»

Авторегистрацию можно разрешить или запретить. Если авторегистрация
разрешена, то при изменении данных регистрация будет выполнена автоматически. Если запрещена, то регистрацию изменения необходимо выполнять
«вручную» (определив код на встроенном языке). Следует отметить, что
можно «корректировать» результат регистрации и в том случае, если авторегистрация объекта включена. Оба действия будут подробнее рассмотрены
несколько позже.
У каждого объекта конфигурации, входящего в состав плана обмена, имеется
свойство ОбменДанными, имеющее тип ПараметрыОбменаДанными.
Данное свойство может быть использовано только для чтения и предназначено как для управления различными параметрами при обмене данными,
так и для реализации других механизмов, связанных с изменением объекта.
Например, такое свойство, как Загрузка, может определять необходимость

Глава 3. Обмен данными

239

проведения (не проведения) каких-либо проверок в момент записи объекта
(при записи в режиме загрузки можно отказаться от проверки номеров, кодов
объектов, наличия каких-либо связанных с загружаемыми объектами данных
и т. п., так как эти данные могут быть загружены после).
Следует обратить внимание на тот факт, что значение данного свойства
не хранится в базе данных. В соответствии с этим все изменения (определения различных коллекций, свойств), которые выполняются через данное
свойство, «работают» только в течение существования объекта.
У объекта ПараметрыОбменаДанными есть свойство Получатели,
имеющее тип НаборУзлов. В данном свойстве хранится перечень узлов, для
которых будет выполняться регистрация изменений при записи или удалении
данных.
Рассмотрим особенности регистрации изменений как при автоматической
регистрации изменений, так и в обратном случае.
Автоматическая регистрация изменений
В случае если для объекта включена автоматическая регистрация изменений,
этот список получателей (свойство Получатели) заполняется автоматически, перед тем как будет вызван обработчик события ПередЗаписью
(при выполнении записи данных) или ПередУдалением (при выполнении
удаления). Перед вызовом данных обработчиков событий список получателей предварительно очищается. Исходя из этого, вносить изменения
в список получателей (если для объекта включена автоматическая регистрация изменений) можно только в обработчиках ПередЗаписью (и/или
ПередУдалением). При этом следует помнить, что список получателей
может содержать только ссылки на узлы, относящиеся к планам обмена,
в состав которых входит соответствующий объект конфигурации.
В приведенном ниже примере обработчик ПередЗаписью исключает из
списка получателей узел с кодом Оптовый плана обмена УдаленныеСклады
(листинг 3.1).
Листинг 3.1. Пример обработчика события «ПередЗаписью»
Процедура ПередЗаписью()
Узел = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Оптовый″);
Объект.ОбменДанными.Получатели.Удалить(Узел);
КонецПроцедуры

240

Технологии интеграции «1С:Предприятия 8.3»

Следует помнить, что значение свойства ОбменДанными не хранится
в информационной базе. Можно сказать, что данное свойство заполняется
некими значениями по умолчанию при создании объекта (в оперативной
памяти). Одним из таких свойств, которое в дальнейшем определяет порядок
регистрации изменений, является свойство Автозаполнение. То есть,
несмотря на тот факт, что при настройке состава регистрации изменений
авторегистрация для объекта была включена, при создании объекта (при
создании, получении из кода, при открытии формы объекта) это свойство
можно переопределить.
Пример реализации приведен в листинге 3.2.
Листинг 3.2. Пример изменения свойства «Автозаполнение»
Узел = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Оптовый″);
Объект.ОбменДанными.Получатели.Автозаполнение = Ложь;
Объект.ОбменДанными.Получатели.Добавить(Узел);
Объект.Записать();

В этом случае автозаполнение отключается и состав коллекции получателей
можно определять в любом возможном месте (участке кода, отвечающего за
работу с объектом). Но, чтобы конфигурация была логически понятна и легко
читаема, лучше это делать все в тех же обработчиках событий ПередЗаписью и/или ПередУдалением.
Также следует напомнить, что в конфигурации может быть определено
несколько планов обмена. В одном из них для объекта может быть включена
автоматическая регистрация изменений, в другом она может быть отключена.
Рассмотрим пример. План обмена УдаленныеОфисы (автоматическая регистрация изменений для документа РасходнаяНакладная включена) имеет
следующий состав непредопределенных узлов:
■■ Центральный склад.
■■ Офис на «Рублевке».
План обмена УдаленныеСклады (автоматическая регистрация изменений для
документа РасходнаяНакладная отключена) имеет следующий состав непредопределенных узлов:
■■ Розничный склад.
■■ Оптовый склад.
Подобная настройка планов обмена может быть выполнена исходя из соображений, что документ РасходнаяНакладная должен присутствовать во всех
офисах компании и при этом должен быть выгружен только на «свой» склад.

Глава 3. Обмен данными

241

При создании документа РасходнаяНакладная (или изменении существующего) в таблице регистрации изменений данного документа появятся две
записи для узлов:
■■ УдаленныеОфисы – Центральный склад.
■■ УдаленныеОфисы − Офис на «Рублевке».
Следует оговориться, что такое поведение системы наблюдается только
в том случае, если не предпринимается никаких шагов по принудительной
регистрации изменений. Кстати, свойство Автозаполнение в коллекции
Получатели в данном примере установлено в значение Истина.
Ручная регистрация изменений
Если автоматическая регистрация изменений не производится, то перед
вызовом обработчиков ПередЗаписью и ПриУдалении сброс и заполнение списка получателей не осуществляются. Исходя из этого, заполнение
данного списка может производиться в любом фрагменте кода, как показано
в листинге 3.3.
Листинг 3.3. Пример заполнения списка узлов
Узел = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Оптовый″);
Объект.ОбменДанными.Получатели.Добавить(Узел);
Объект.Записать();

Если автоматическая регистрация изменений для объекта отключена
и в конфигурации нигде не встречаются строки кода, подобные тем, которые
приведены выше (или будут рассматриваться ниже), экземпляры данного
объекта никогда не попадут в таблицу регистрации изменений (для данного
объекта она будет пустой).
Вернемся к примеру с несколькими планами обмена (условия – в предыдущем разделе).
Несмотря на тот факт, что в свойстве Автозаполнение коллекции Получатели (свойства ОбменДанными) установлено значение Истина, для
непредопределенных узлов, определенных в плане обмена УдаленныеСклады,
регистрация изменений не проводится. Но список получателей перед вызовом
обработчиков событий ПередЗаписью и ПередУдалением очищается.
Исходя из этого, для ручной регистрации изменений можно в модуле объекта
(документа) определить следующий обработчик события (листинг 3.4).

242

Технологии интеграции «1С:Предприятия 8.3»

Листинг 3.4. Пример обработчика события «ПередЗаписью»
Процедура ПередЗаписью(Отказ)
Если ОбменДанными.Получатели.Автозаполнение Тогда

ВключитьРегистрацию(ОбменДанными, Склад);

// Проверить, изменился ли склад в уже существующем документе.

Если Не ЭтоНовый() Тогда

Запрос = Новый Запрос(″

|ВЫБРАТЬ

|
Склад

|ИЗ

|
Документ.РасходнаяНакладная

|ГДЕ

|
Ссылка = &ТекСсылка″);

Запрос.УстановитьПараметр(″ТекСсылка″, Ссылка);

Выборка = Запрос.Выполнить().Выбрать();

Выборка.Следующий();

Если Выборка.Склад СкладТогда

ВключитьРегистрацию(ОбменДанными, Выборка.Склад);

КонецЕсли;

КонецЕсли;
КонецЕсли;

КонецПроцедуры

Процедура для регистрации изменений объекта в конкретном узле обмена
будет выглядеть следующим образом (листинг 3.5).
Листинг 3.5. Процедура «ВключитьРегистрацию()»
Процедура ВключитьРегистрацию(ОбменДанными, Склад) Экспорт
// Получить список узлов для конкретного склада.
Запрос = Новый Запрос;
Запрос.Текст = ″ВЫБРАТЬ

| УдаленныеСклады.Ссылка

|ИЗ

| ПланОбмена.УдаленныеСклады КАК УдаленныеСклады

|ГДЕ

|
(УдаленныеСклады.Склад = &Склад

|
ИЛИ УдаленныеСклады.Склад = &ПустойСклад)

|
И УдаленныеСклады.ЭтотУзел = ЛОЖЬ″;
Запрос.УстановитьПараметр(″Склад″, Склад);
Запрос.УстановитьПараметр(″ПустойСклад″,Справочники.Склады.ПустаяСсылка());
Выборка = Запрос.Выполнить().Выбрать();
// Зарегистрировать изменения для выбранных узлов.
Пока Выборка.Следующий() Цикл

ОбменДанными.Получатели.Добавить(Выборка.Ссылка);
КонецЦикла;
КонецПроцедуры

Глава 3. Обмен данными

243

Обе рассмотренные процедуры одновременно решают две задачи:
■■ Регистрируют изменения для узла, у которого реквизит Склад совпадает
со складом, указанным в документе.
■■ Производят проверку (для ранее существовавших документов), не изменился ли склад. Если склад изменился, то производится регистрация
изменения для узла, значение реквизита Склад которого соответствует
старому значению документа (хранимому на момент проверки в информационной базе). Далее в момент выгрузки изменений данная регистрация
может быть «подменена» на объект УдалениеОбъекта, что приведет
к удалению накладной, «не свойственной» узлу.
Принудительная регистрация изменений
В некоторых случаях может потребоваться принудительная регистрация
изменений для какого-либо объекта, их списка (или всех объектов). Для
этой цели может использоваться метод ЗарегистрироватьИзменения()
объекта ПланыОбменаМенеджер. Данный метод позволяет выполнять
регистрацию изменений одиночных элементов данных или целых групп для
одного или нескольких узлов.
Первый параметр данного метода – ссылка на узел плана обмена или массив
ссылок на узлы, для которых выполняется регистрация изменений. Если
первый параметр представляет собой одиночную ссылку на узел, то второй
параметр может быть опущен. При этом выполняется регистрация изменений
всех элементов данных, которые на данный момент присутствуют в базе
данных и изменения которых могут быть зарегистрированы для данного узла
(листинг 3.6).
Листинг 3.6. Регистрация изменений данных для указанного узла
Узел = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Новый″);
ПланыОбмена.ЗарегистрироватьИзменения(Узел);

Данный вариант использования метода может быть полезен для организации
начальной передачи данных вновь созданному узлу.
Если же первый параметр представляет собой массив ссылок на узлы, то
второй параметр обязательно должен быть указан (листинг 3.7).
Листинг 3.7. Регистрация изменений элемента данных для указанных узлов
Узлы = Новый Массив(2);
Узлы[0] = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Оптовый″);
Узлы[1] = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Розничный″);
Данные = Справочники.Номенклатура.НайтиПоКоду(″000000004″);
ПланыОбмена.ЗарегистрироватьИзменения(Узлы, Данные);

244

Технологии интеграции «1С:Предприятия 8.3»

Впрочем, второй параметр может присутствовать и в том случае, если первый
параметр – одиночная ссылка на узел. В зависимости от способа задания
второго параметра можно зарегистрировать изменения одного элемента
данных или же всех данных, относящихся к одному объекту конфигурации.
Кроме того, можно зарегистрировать удаление объекта (указав в качестве
второго параметра объект типа УдалениеОбъекта).
Для регистрации изменений одного элемента в качестве второго параметра
может быть указан сам элемент данных, ссылка на объект базы данных или
объект конфигурации.
Пример регистрации изменений всех данных, относящихся к объекту конфигурации, приведен в листинге 3.8.
Листинг 3.8. Регистрация изменений данных, относящихся к объекту конфигурации
Узлы = Новый Массив(2);
Узлы[0] = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Оптовый″);
Узлы[1] = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Розничный″);
ПланыОбмена.ЗарегистрироватьИзменения(Узлы, Метаданные.Справочники.Номенклатура);

До сих пор ничего не говорилось об очистке таблицы регистрации изменений. По большому счету существуют две основные стратегии выполнения
данного действия:
■■ Гарантированная доставка сообщений. В этом случае очистка таблиц
регистрации изменений производится сразу же после того, как сообщение
будет сформировано.
■■ Ожидание квитанции. В этом случае очистка таблиц регистрации изменений производится при приеме сообщения, исходя из номера последнего
полученного «от нас» сообщения (этот номер передается как квитанция
в заголовке сообщения).
Рассмотрим следующий пример. Считаем, что таблица регистрации изменений
документа РасходнаяНакладная имеет следующее наполнение (табл. 3.10).
Таблица 3.10. Таблица регистрации изменений
ДокументСсылка.РасходнаяНакладная

Расходная накладная № 1 от…
Расходная накладная № 1 от…
Расходная накладная № 2 от…
Расходная накладная № 2 от…
Расходная накладная № 3 от…
Расходная накладная № 3 от…
Расходная накладная № 4 от…
Расходная накладная № 4 от…

Узел

Оптовый
Розничный
Оптовый
Розничный
Оптовый
Розничный
Оптовый
Розничный

Номер сообщения

3
Null
1
1
2
1
3
Null

Глава 3. Обмен данными

245

Исходя из данных таблицы, можно сказать, что в узел Оптовый было отправлено три сообщения, в узел Розничный − одно.
Используется стратегия ожидания подтверждающей квитанции, при этом
от участника обмена Оптовый приходит сообщение, в котором указано, что
последнее полученное от «нас» сообщение имело номер 2. Исходя из этой
информации, можно удалить изменения, отправленные в сообщениях 1 и 2
(поле НомерСообщения таблицы регистрации изменений).
Таблица примет следующий вид (табл. 3.11).
Таблица 3.11. Таблица регистрации изменений
ДокументСсылка.РасходнаяНакладная

Узел

Номер сообщения

Расходная накладная № 1 от…

Оптовый

3

Расходная накладная № 1 от…

Розничный

Null

Расходная накладная № 2 от…

Розничный

1

Расходная накладная № 3 от…

Розничный

1

Расходная накладная № 4 от…

Оптовый

3

Расходная накладная № 4 от…

Розничный

Null

В случае гарантированной доставки таблица регистрации изменений очищается сразу после отправки сообщения. Например, таблица регистрации
изменений имела следующий вид (табл. 3.12).
Таблица 3.12. Таблица регистрации изменений
ДокументСсылка.РасходнаяНакладная

Узел

Номер сообщения

Расходная накладная № 1 от…

Оптовый

3

Расходная накладная № 1 от…

Розничный

Null

Расходная накладная № 2 от…

Оптовый

1

Расходная накладная № 3 от…

Оптовый

2

Расходная накладная № 4 от…

Оптовый

3

Расходная накладная № 4 от…

Розничный

Null

В узел Розничный формируется сообщение с номером 2. После того как
сообщение сформировано, таблица регистрации будет иметь следующий вид
(табл. 3.13).

246

Технологии интеграции «1С:Предприятия 8.3»

Таблица 3.13. Таблица регистрации изменений
ДокументСсылка.РасходнаяНакладная

Узел

Номер сообщения

Расходная накладная № 1 от…

Оптовый

3

Расходная накладная № 1 от…

Розничный

2

Расходная накладная № 2 от…

Оптовый

1

Расходная накладная № 3 от…

Оптовый

2

Расходная накладная № 4 от…

Оптовый

3

Расходная накладная № 4 от…

Розничный

2

Далее производится вызов метода по удалению записей в таблице
регистрации изменений (метод рассматривается чуть позже). Таблица регистрации изменений примет такой вид (табл. 3.14).
Таблица 3.14. Таблица регистрации изменений
ДокументСсылка.РасходнаяНакладная

Узел

Номер сообщения

Расходная накладная № 1 от…

Оптовый

3

Расходная накладная № 2 от…

Оптовый

1

Расходная накладная № 3 от…

Оптовый

2

Расходная накладная № 4 от…

Оптовый

3

Для удаления записей регистрации изменений у менеджера планов обмена
имеется метод УдалитьРегистрациюИзменений(). В качестве первого
параметра метода может использоваться ссылка на узел или массив таких
ссылок. В качестве второго параметра может указываться ссылка на объект,
УдалениеОбъекта, объект конфигурации, число (номер сообщения).
Для удаления записей таблицы регистрации изменений с номерами
сообщения не больше переданного (в примере с номерами 1 и 2) может
использоваться следующий фрагмент кода (листинг 3.9).
Листинг 3.9. Пример удаления записей таблицы регистрации
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайла);
// Загрузить сообщение обмена из файла и начать чтение сообщения.
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
// Удалить регистрацию с номерами сообщения не больше принятого (2).
НомерПринятого = ЧтениеСообщения.НомерПринятого;
ПланыОбмена.УдалитьРегистрациюИзменений(Узел, НомерПринятого);

Глава 3. Обмен данными

247

Пример удаления записей регистрации изменений для всех данных, которые
зарегистрированы для узла, приведен в листинге 3.10.
Листинг 3.10. Пример удаления записей таблицы регистрации
Узел = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Оптовый″);
ПланыОбмена.УдалитьРегистрациюИзменений(Узел);

Можно удалить записи регистрации изменений конкретного элемента данных
для одного или нескольких узлов (листинг 3.11).
Листинг 3.11. Пример удаления записей таблицы регистрации
Узлы = Новый Массив(2);
Узлы[0] = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Оптовый″);
Узлы[1] = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Розничный″);
Данные = Справочники.Номенклатура.НайтиПоКоду(″000000004″);
ПланыОбмена.УдалитьРегистрациюИзменений(Узлы, Данные);

Также можно удалить записи регистрации изменений всех данных, относящихся к объекту конфигурации, для одного или нескольких узлов (листинг
3.12).
Листинг 3.12. Пример удаления записей таблицы регистрации
Узлы = Новый Массив(2);
Узлы[0] = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Оптовый″);
Узлы[1] = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Розничный″);
ПланыОбмена.УдалитьРегистрациюИзменений(Узлы,
Метаданные.Справочники.Номенклатура);

Инфраструктура сообщений
Важнейшей составляющей инфраструктуры сообщений являются сами сообщения. Как уже отмечалось, сообщения передаются в рамках плана обмена
от одного узла другому. То есть каждое сообщение точно ассоциировано
с планом обмена, имеет одного отправителя и одного получателя.
Рассмотрим, что такое сообщение. Сообщение оформляется как документ
XML, имеющий определенную структуру. В качестве примера рассмотрим
следующее сообщение (листинг 3.13).
Листинг 3.13. Пример сообщения обмена



УдаленныеСклады

Оптовый

ЦентрОфис

248

Технологии интеграции «1С:Предприятия 8.3»


20

15







Все сообщение находится внутри элемента XML с именем Message, относящимся к пространству имен http://v8.1c.ru/messages. Сообщение делится
на заголовок Header и тело сообщения Body. Оба относятся к пространству
имен http://v8.1c.ru/messages.
Структура заголовка жестко задана. Информация заголовка представлена в нескольких элементах XML, вложенных в элемент Header.
Все элементы, вложенные в элемент Header, относятся к пространству имен
http://v8.1c.ru/messages.
■■ Элемент с именем ExchangePlan содержит имя плана обмена, к которому
относится сообщение.
■■ Элемент с именем From содержит код узла-отправителя.
■■ Элемент с именем To содержит код узла, для которого предназначено
сообщение.
■■ Элемент с именем MessageNo содержит номер данного сообщения. Номер
сообщения является положительным целым числом и присваивается
узлом-отправителем. Номер каждого последующего сообщения равен
номеру предыдущего отправленного сообщения плюс 1.
■■ Элемент с именем ReceivedNo содержит максимальный номер сообщения,
которое узел-отправитель данного сообщения принял от узла-получателя
данного сообщения. Данное значение включено в состав заголовка сообщения для подтверждения приема сообщений.
Тело сообщения содержится в элементе XML с именем Body, относящемся
к пространству имен http://v8.1c.ru/messages. Данный элемент может иметь
произвольное содержимое, определяемое прикладными потребностями.
Инфраструктурой сообщений содержимое тела сообщения никак не регламентируется.
Сообщение для выгрузки данных может быть создано следующим образом
(листинг 3.14).

Глава 3. Обмен данными

249

Листинг 3.14. Пример сообщения для выгрузки данных
ЗаписьXML = Новый ЗаписьXML();
ЗаписьXML.ОткрытьФайл(″c:\temp\выгрузка.xml″);
ЗаписьXML.ЗаписатьОбъявлениеXML();
Узел = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(″Оптовый″);
// Создать объект ″ЗаписьСообщенияОбмена″.
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, Узел);
// Записать тело сообщения.
// …
ЗаписьСообщения.ЗакончитьЗапись();
ЗаписьXML.Закрыть();

Выгрузка данных сопровождается созданием объекта типа ЗаписьСообщенияОбмена (переменная ЗаписьСообщения). Сразу после создания
этот объект еще «не знает» своего номера, получателя и т. п. Вся подобная
информация указывается при выполнении метода НачатьЗапись().
В качестве первого параметра передается объект ЗаписьXML, в качестве
второго – ссылка на узел плана обмена (ссылка на получателя).
При выполнении этого метода сообщению присваивается номер, определяемый как номер предыдущего отправленного сообщения, увеличенный
на 1 (информация берется из узла-получателя). Производится запись
в XML-документ заголовка сообщения, а также записывается начало
элемента XML, соответствующего телу сообщения. Устанавливается блокировка на запись базы данных, соответствующая узлу плана обмена.
Завершить запись сообщения можно с помощью двух методов объекта
ЗаписьСообщенияОбмена:

■■ ЗакончитьЗапись(),
■■ ПрерватьЗапись().
Вызов метода ПрерватьЗапись() прерывает запись сообщения, при этом
оно считается неотправленным (счетчик отправленных сообщений в итоге
не увеличивается). Неявный вызов этого метода происходит также в случае
повторного вызова метода НачатьЗапись() (без последующего вызова
метода ЗакончитьЗапись()) либо при разрушении (окончании времени
жизни переменной, связанной с объектом) объекта ЗаписьСообщенияОбмена.

Метод ЗакончитьЗапись() осуществляет нормальное завершение записи
сообщения. В этом случае в сообщение записывается конец элемента XML
(представляющего тело сообщения). В узел плана обмена записывается

250

Технологии интеграции «1С:Предприятия 8.3»

номер сообщения обмена данными. С записи узла плана обмена снимается
блокировка, и сообщение считается отправленным.
Для чтения записанного в файл сообщения обмена в базе узла-получателя
может использоваться следующий фрагмент кода (листинг 3.15).
Листинг 3.15. Пример чтения сообщения обмена
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(″c:\temp\выгрузка.xml″);
// Создать объект ″ЧтениеСообщенияОбмена″.
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
ЧтениеСообщения.НачатьЧтение(ЧтениеXML, ДопустимыйНомерСообщения.Больший);
// Прочитать тело сообщения.
// …
ЧтениеСообщения.ЗакончитьЧтение();
ЧтениеXML.Закрыть();

После открытия XML-документа, содержащего сообщение, создается объект
ЧтениеСообщенияОбмена. Чтение данных начинается с выполнения
метода НачатьЧтение() объекта ЧтениеСообщенияОбмена. При этом
производится чтение заголовка сообщения обмена данными, проверяются
содержащиеся в заголовке данные. Если какие-либо данные указаны неправильно (задан неизвестный план обмена, указан узел, не входящий в план
обмена, номер сообщения не соответствует ожидаемому), инициируется
исключение.
При начале чтения сообщения устанавливается блокировка на запись узла
плана обмена. Используя второй параметр метода, можно установить вариант
ожидаемого номера сообщения:
■■ любой (фактически контроль номеров сообщения не производится);
■■ очередной (строго больший на единицу, чем предыдущий);
■■ больший.
Следует обратить внимание на тот факт, что в приведенном фрагменте кода
нет ни указания на используемый план обмена (их в конфигурации может
быть несколько), ни указания на узел-отправитель. Данная информация
получается из заголовка сообщения (в случае невозможности определения,
как и было сказано выше, возникает исключение).
Завершить чтение сообщения можно с использованием двух методов:
■■ ЗакончитьЧтение();
■■ ПрерватьЧтение().

Глава 3. Обмен данными

251

Обращение к методу ПрерватьЧтение() вызывает немедленное прерывание чтения сообщения. Блокировка с записи узла плана обмена снимается.
В соответствующий узел плана обмена не вносится никаких изменений
(не изменяется номер последнего принятого сообщения). Этот же метод
вызывается неявно при разрушении (окончании времени жизни переменной,
связанной с данным объектом) объекта ЧтениеСообщенияОбмена или
перед повторным вызовом метода НачатьЧтение().
Выполнение метода ЗакончитьЧтение() вызывает попытку нормального
завершения чтения сообщения. При этом проверяется нормальное завершение сообщения (текущим элементом в объекте ЧтениеXML является конец
элемента, реализующего само сообщение). Номер принятого сообщения
помещается в реквизит НомерПринятого узла плана обмена. Блокировка
записи узла плана обмена снимается, и сообщение считается принятым.

Распределенные информационные базы
Распределенная информационная база представляет собой иерархическую структуру, состоящую из отдельных информационных баз
«1С:Предприятия» – узлов распределенной информационной базы, между
которыми организован обмен данными с целью синхронизации конфигурации и данных.
В основе механизмов распределенных информационных баз лежат
универсальные механизмы обмена данными, но они содержат некоторые
дополнительные возможности, недоступные через универсальные механизмы.
Главное отличие распределенных информационных баз от универсальных
механизмов обмена данными заключается в том, что универсальные механизмы позволяют выстраивать достаточно произвольные схемы обмена
данными, в то время как распределенные информационные базы имеют
более узкую специализацию.

Общие принципы
Распределенная информационная база – это совокупность информационных
баз «1С:Предприятия» (узлов распределенной информационной базы),
в которых поддерживается синхронизация конфигурации и данных. Распределенная информационная база имеет иерархическую структуру. У каждого
узла распределенной информационной базы может быть один главный
и произвольное число подчиненных узлов. «Самый главный узел», или узел,
у которого нет главного узла, называется корневым узлом распределенной

252

Технологии интеграции «1С:Предприятия 8.3»

информационной базы. Каждый из узлов может обмениваться данными
только со своими «соседями», то есть со своими главным и подчиненными
узлами (рис. 3.6).

Рис. 3.6. Структура узлов распределенной информационной базы

Изменения конфигурации допускаются только в корневом узле распределенной информационной базы с последующим ее распространением по
иерархии от корневого узла к его подчиненным и т. д. Таким образом, механизм управления распределенными информационными базами обеспечивает
наличие во всех узлах распределенной информационной базы одной и той же
конфигурации.
Изменение данных допускается в любом узле распределенной информационной базы. Синхронизация данных достигается путем распространения
изменений данных, произведенных в одном узле, во все структуры распределенной информационной базы (рис. 3.7).

Рис. 3.7. Миграция данных в распределенной информационной базе

Глава 3. Обмен данными

253

Если в рамках всей распределенной информационной базы поддерживается полная идентичность конфигурации, то полная идентичность данных
не обязательна. Состав данных, изменения которых передаются в рамках
распределенной информационной базы, может регулироваться как
«по вертикали» (путем определения множества объектов конфигурации,
данные которых участвуют в обмене), так и «по горизонтали» (путем задания
условий на передачу и прием изменений на уровне отдельных элементов
данных).
Планы обмена занимают центральное место в управлении распределенными
информационными базами. Но для того, чтобы тот или иной план обмена
мог использоваться для организации распределенной информационной базы,
у него при конфигурировании должно быть установлено свойство Распределенная информационная база.
Данные в распределенной информационной базе переносятся с помощью
сообщений, предоставляемых инфраструктурой сообщений. В отличие от
универсальных механизмов обмена данными содержимое сообщений, передаваемых между узлами распределенной информационной базы, не может
быть произвольным, а является регламентированным протоколом обмена,
принятым для распределенной информационной базы.

Состав данных, изменениями которых будет производиться обмен в рамках
распределенной информационной базы, определяется составом плана
обмена. Вхождение объекта конфигурации в состав плана обмена показывает, что изменения данных, соответствующих объекту конфигурации,
могут регистрироваться для узлов данного плана обмена. Но, в отличие от
универсальных механизмов обмена данными, номенклатура данных, обмен
которыми может производиться в рамках распределенной информационной
базы, строго ограничена составом соответствующего плана обмена.
Для регистрации изменений данных в распределенной информационной
базе задействована служба регистрации изменений. Элементы данных помещаются в сообщение с использованием механизмов XML-сериализации.
Помимо изменений данных между узлами распределенной информационной
базы передаются изменения конфигурации, а также некоторая дополнительная служебная информация. Регистрация изменений конфигурации
и включение их в сообщение обмена в распределенной информационной базе
осуществляются полностью автоматически и недоступны для пользователя
и разработчика конфигураций.
В отличие от универсальных механизмов обмена данными формирование
и прием сообщения обмена данными в распределенной информационной
базе производятся «в одно действие», то есть все содержимое сообщения
формируется путем вызова одного метода встроенного языка (листинг 3.16).

254

Технологии интеграции «1С:Предприятия 8.3»

Листинг 3.16. Пример вызова метода «ЗаписатьИзменения()»
ПланыОбмена.ЗаписатьИзменения(ЗаписьСообщения, 0);

Считывание содержимого сообщения производится также путем вызова
одного метода (листинг 3.17).
Листинг 3.17. Пример вызова метода «ПрочитатьИзменения()»
ПланыОбмена.ПрочитатьИзменения(ЧтениеСообщения);

Пример использования данных методов приведен в разделе «Запись и чтение
сообщений обмена».
Для того чтобы управлять составом данных, помещаемых в сообщение, а
также считываемых из сообщения и помещаемых в базу данных, на уровне
отдельных элементов данных в модуле плана обмена могут быть определены
обработчики событий:
■■ ПриОтправкеДанныхПодчиненному,
■■ ПриОтправкеДанныхГлавному,
■■ ПриПолученииДанныхОтПодчиненного,
■■ ПриПолученииДанныхОтГлавного.
Таким образом, в распределенной информационной базе практически полностью задействованы универсальные механизмы обмена данными, но имеются
и некоторые дополнительные возможности, недоступные вне распределенной
информационной базы.

Главный и подчиненный узлы
Как было указано выше, у каждого из узлов распределенной информационной базы может быть один главный и произвольное число подчиненных
узлов. Для своего главного узла узел является подчиненным и, соответственно, для своих подчиненных – главным. Узел, у которого нет главного
узла, является корневым узлом распределенной информационной базы.
Корневой узел распределенной информационной базы – это единственное
место, где разрешено вносить изменения в конфигурацию информационной
базы.
Распределенная информационная база может быть построена на основе
нескольких планов обмена, с установленным свойством Распределенная
информационная база. Взаимодействие в каждой паре узлов «главный –
подчиненный» производится в соответствии с одним из определенных

Глава 3. Обмен данными

255

в конфигурации планов обмена. Никаких ограничений на использование того
или иного плана обмена в том или ином узле распределенной информационной базы не накладывается.
Каждый из узлов распределенной информационной базы, как и в случае
использования универсальных механизмов обмена данными, «знает» только
своих «соседей», то есть свой главный и свои подчиненные узлы (рис. 3.8).

Рис. 3.8. Состав узлов распределенной информационной базы
для узла «Розничный магазин»

Таким образом, полная схема распределенной информационной базы при
наличии более чем двух уровней неизвестна никакому из узлов. Это является
особенностью службы регистрации изменений: изменения регистрируются
для всех непредопределенных узлов, если в каком-либо плане обмена указать
все узлы распределенной базы, то это нарушит структуру подчинения баз
данных.

Сообщение обмена данными
в распределенной информационной базе
Для передачи изменений данных и конфигурации в распределенной информационной базе используются сообщения обмена данными, предоставляемые
инфраструктурой сообщений. Если в случае применения универсальных
механизмов обмена данными разработчик конфигурации сам определяет, что и как помещается в тело сообщения, то в случае распределенной

256

Технологии интеграции «1С:Предприятия 8.3»

информационной базы структура и состав данных, помещаемых в тело сообщения, четко определены.
Рассмотрим структуру сообщения обмена данными, используемого в распределенной информационной базе. В качестве примера рассмотрим следующее
сообщение (листинг 3.18).
Листинг 3.18. Пример структуры сообщения обмена


УдаленныеСклады
Склад1
Офис
20
15



7b4d5320 f69c 4a7b 9273 ff56607fc8ab


88d3f3a6ba3f4df03c7ec00f154837fc
00cf636b02a488103a64c7a2cf81069e




























Глава 3. Обмен данными

257

Как видно из примера, все особенности сообщения обмена данными, используемого в распределенной информационной базе, сосредоточены в теле
сообщения. Тело сообщения (элемент Body, относящийся к пространству
имен http://v8.1c.ru/messages) содержит один-единственный элемент XML –
Changes, относящийся к пространству имен http://v8.1c.ru/dataexchange. Внутри
этого элемента сосредоточены все данные, передаваемые при обмене
данными в распределенной информационной базе.
Элемент Changes может содержать следующие вложенные элементы:
■■ Элемент Signature содержит «подпись» плана обмена, в соответствии
с которым получено сообщение.
■■ Элемент Config содержит изменения конфигурации, а также данные,
идентифицирующие
состояние
конфигурации.
Необязательные
элементы Metadata, вложенные в Config, содержат изменения отдельных
объектов конфигурации. Если изменения конфигурации не передаются в сообщении, то элементы Metadata отсутствуют. Такие элементы
могут присутствовать только в сообщениях, передаваемых от главного
узла подчиненному. Элементы Digest1 и Digest2 содержат цифровые
подписи передаваемых в данном сообщении изменений конфигурации
и всей конфигурации за вычетом изменений. Элементы Digest1 и Digest2
присутствуют во всех сообщениях: передаваемых от главного узла подчиненному и наоборот.
■■ Необязательный элемент ConfigurationExtensions содержит информацию
о расширениях, которые могут передаваться в рамках распределенной
информационной базы. В данном элементе содержится несколько
вложенных элементов ConfigurationExtension, каждый из которых описывает одно расширение. Также в качестве подчиненного элемента может
выступать элемент ConfigurationExtensionDeletion, который содержит идентификатор расширения, которое должно быть удалено в информационной
базе-приемнике данного сообщения обмена. Подробнее о расширениях
в распределенной информационной базе рассказывается в разделе «Обмен
данными в распределенной информационной базе».
■■ Элемент Nodes может присутствовать только в сообщениях, передаваемых
от главного узла подчиненному. Этот элемент содержит два вложенных
элемента Node, первый из которых содержит данные главного узла (отправителя), а второй – подчиненного (получателя).
■■ И, наконец, элемент Data содержит измененные элементы данных, передаваемые в сообщении. Элементы данных помещаются в сообщение
с помощью XML-сериализации.

258

Технологии интеграции «1С:Предприятия 8.3»

Работа с распределенной информационной базой
В работе с распределенной информационной базой можно выделить следующие основные действия:
■■ создание начального образа подчиненного узла распределенной информационной базы;
■■ запись сообщения обмена данными для отправки в другой узел распределенной информационной базы;
■■ чтение сообщения обмена данными, отправленного из другого узла
распределенной информационной базы.
Если для доступа к возможностям, предоставляемым универсальными
механизмами обмена данными, без встроенного языка не обойтись, то перечисленные действия могут быть выполнены как из встроенного языка, так
и интерактивно с помощью команд меню Еще формы списка плана обмена
или иными средствами, определенными при конфигурировании.
Создание начального образа
Для того чтобы интерактивно создать начальный образ для узла плана
обмена, необходимо открыть форму списка плана обмена или непосредственно форму узла плана обмена. Для выбранного (непредопределенного)
узла плана обмена следует выполнить команду Создать начальный образ,
которая доступна в командной панели формы, а также в меню Еще (рис. 3.9).

Рис. 3.9. Интерактивное создание начального образа

При этом на экране появится диалог создания начального образа,
в котором будет предложено заполнить все необходимые параметры информационной базы – нового узла распределенной информационной базы.
Структура диалога создания начального образа узла распределенной
информационной базы аналогична структуре диалога создания новой
информационной базы. После заполнения всех необходимых параметров
платформа «1С:Предприятие» выполнит создание начального образа:

Глава 3. Обмен данными

259

■■ Будет создана новая информационная база с указанными параметрами.
■■ Конфигурация распределенной информационной базы будет перенесена
во вновь созданную информационную базу.
■■ Будет выполнен перенос данных текущей информационной базы во
вновь созданную, при этом для каждого элемента данных будет вызван
обработчик события узла плана обмена ПриОтправкеДанныхПодчиненному (т. е. будут перенесены только те данные, которые входят
в состав соответствующего плана обмена, с учетом всех ограничений,
фильтров, которые определены в обработчике события ПриОтправкеДанныхПодчиненному).
■■ В новом узле распределенной информационной базы будут заполнены
данные узла ЭтотУзел плана обмена данными узла данной информационной базы, для которого создается начальный образ. Этот узел будет
предопределенным для базы подчиненного узла.
■■ В новом узле распределенной информационной базы будет создан
элемент плана обмена, соответствующий данной информационной базе.
Его данные будут перенесены. Затем ссылка на этот элемент будет установлена в качестве значения главного узла в этой информационной базе.
■■ Будут удалены все записи о регистрации изменений для узла, начальный
образ которого был только что создан.
Процесс создания начального образа при помощи метода СоздатьНачальныйОбраз() менеджера планов обмена аналогичен интерактивной
процедуре – за исключением того, что все параметры новой информационной базы, а также узел, для которого необходимо создать начальный образ,
должны быть переданы в качестве параметров при вызове указанной процедуры (листинг 3.19).
Листинг 3.19. Пример создания начального образа
Узел = ПланыОбмена.Магазины.НайтиПоКоду(″Оптовый″);
ПланыОбмена.СоздатьНачальныйОбраз(Узел, СтрокаСоединения);

В строковой переменной СтрокаСоединения указывается информационная база, в которой будет создан начальный образ подчиненного узла
распределенной информационной базы. Информационная база для создания
начального образа должна быть пустой или не должна существовать вовсе.
Строка соединения представляет собой набор параметров, каждый из
которых представляет собой фрагмент вида =, где имя
− имя параметра, а − его значение. Фрагменты отделяются
друг от друга символами «;». Если значение содержит пробельные символы,
то оно должно быть заключено в двойные кавычки («).

260

Технологии интеграции «1С:Предприятия 8.3»

Запись и чтение сообщений обмена
Если план обмена используется в реализации механизма распределенных
информационных баз, то в форме списка узлов данного плана обмена
для непредопределенного узла, в командной панели, а также в меню Еще
доступны две команды по записи и чтению сообщения обмена: Записать изменения, Прочитать изменения (рис. 3.10).

Рис. 3.10. Интерактивная запись и чтение изменений

При выборе команды (например, записи изменений) открывается диалог,
в котором указывается количество элементов данных, обрабатываемых в одной транзакции, и устанавливается флажок Сжимать сообщение
(XML-документ будет упакован в архив ZIP) – рис. 3.11.

Рис. 3.11. Диалог записи сообщения обмена

После нажатия кнопки Записать и сохранить в файл нужно указать имя файла
сообщения. Если файл был упакован в архив ZIP, то при чтении сообщения
обмена из этого файла архив будет распакован.
К недостатку данного метода следует отнести тот факт, что в данном случае
нет возможности отследить окончание как процесса выгрузки, так и процесса
загрузки данных из сообщения обмена.
Аналогичное действие можно реализовать и самостоятельно, используя для
записи следующий фрагмент кода (листинг 3.20).

Глава 3. Обмен данными

261

Листинг 3.20. Пример записи сообщения обмена
ЗаписьXML.ОткрытьФайл(″c:\temp\out.xml″);
ЗаписьXML.ЗаписатьОбъявлениеXML();
// Создать новое сообщение.
Узел = ПланыОбмена.Магазины.НайтиПоКоду(″Магазин″);
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, Узел);
// Записать изменения.
ПланыОбмена.ЗаписатьИзменения(ЗаписьСообщения, 1000);
ЗаписьСообщения.ЗакончитьЗапись();
ЗаписьXML.Закрыть();

Пример чтения сообщения обмена приведен в листинге 3.21.
Листинг 3.21. Пример чтения сообщения обмена
// Создать объект чтения XML.
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(″c:\temp\out.xml″);
// Загрузить сообщение из файла.
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
// Прочитать изменения.
ПланыОбмена.ПрочитатьИзменения(ЧтениеСообщения,1000);
ЧтениеСообщения.ЗакончитьЧтение();
ЧтениеXML.Закрыть();

Данный подход позволяет отследить момент окончания процесса загрузки/
выгрузки и при необходимости выполнить какие-либо действия.
При необходимости произвести упаковку полученного файла сообщений
(или извлечение сообщения из архива) можно воспользоваться функциональностью платформы по работе с ZIP-архивами, рассмотренной в разделе
«ZIP-архивы».

Подготовка конфигурации к работе
в распределенной информационной базе
Конфигурация, разработанная для обычной информационной базы,
в большинстве случаев требует адаптации для работы в распределенной
информационной базе. В этом разделе мы рассмотрим основные методы
адаптации конфигурации к работе в распределенной информационной базе.

262

Технологии интеграции «1С:Предприятия 8.3»

Описанные методы не являются исчерпывающими, ими можно ограничиться,
когда необходимо обеспечить простой обмен данными. Например, разделение на два офиса с полным дублированием информации в них.
Если же требуется реализация специфической схемы обмена, то может
понадобиться реализация сложной архитектуры распределенного решения,
влекущая за собой серьезную переработку конфигурации. Предположим,
разрабатывается схема обмена, в которой данные из организаций холдинга
должны передаваться в центральную информационную базу в виде сводных
данных за определенный период. В этом случае требуется реализация
в конфигурации соответствующих объектов для хранения детальной
и сводной информации.
Одной из особенностей работы в модели распределенной информационной
базы является возможность создания в различных узлах объектов (документов, элементов справочника, видов характеристик и т. д.), обладающих
одинаковыми номерами (кодами). Это может привести к возникновению
в одной информационной базе объектов с одинаковыми номерами (кодами)
и привести к определенным проблемам.
Дело в том, что синхронизация объектов ведется по уникальному идентификатору. При создании двух и более одинаковых объектов в различных
информационных базах можно определить у них одинаковые значения реквизитов, одинаковые наборы данных в табличных частях, но при этом значения
уникальных идентификаторов (если механизм назначения уникальных
идентификаторов не менялся разработчиком конфигурации) у них разные.
В процессе загрузки такие объекты будут считаться разными. И в этих
случаях для справочника могут создаться элементы с одинаковым кодом, для
документов могут создаться экземпляры с одинаковым номером в определенном периоде и т. д.
Когда назначение номера (кода) выполняется системой автоматически, для
подобных объектов необходимо ввести префиксацию, идентифицирующую
место создания объекта. Для объектов, номера (коды) которых устанавливаются вручную, подобную префиксацию реализовывать не нужно (например,
коды счетов назначаются пользователями вручную, добавление префикса для
них является неправильным). Для объектов, требующих наличия префикса
в номере (коде), необходимо:
■■ Установить тип номера (кода) в значение Строка.
■■ Добавить в конфигурацию объект конфигурации Константа, в котором
будет установлено значение префикса номера (кода), однозначно идентифицирующее данный узел распределенной информационной базы
(значение константы должно быть уникальным среди всех узлов распре-

Глава 3. Обмен данными

263

деленной информационной базы, сама константа не должна быть
включена в состав плана обмена, на базе которого развернута распределенная информационная база).
■■ Реализовать в модулях объектов обработчики событий, выполняемые при установке номера (кода) − ПриУстановкеНовогоНомера
(ПриУстановкеНовогоКода).
Реализация
должна
использовать
в качестве значения префикса значение описанной ранее константы,
листинг 3.22.
Листинг 3.22. Пример назначения префикса
Процедура ПриУстановкеНовогоКода(СтандартнаяОбработка, Префикс)
Префикс = Константы.ПрефиксНомеров.Получить();
КонецПроцедуры

Работа конфигурации в распределенной информационной базе подразумевает обмен данными между узлами распределенной информационной базы.
В процессе обмена порядок следования данных в сообщениях обмена не определен. При чтении данных могут возникать ситуации, при которых считанные
данные ссылаются на несуществующие (возможно, еще не загруженные)
данные. При записи считанных данных в информационную базу необходимо учесть возможность наличия неразрешенных ссылок в записываемых
данных. Для этого в обработчиках событий ПередЗаписью, ПриЗаписи
и ПередУдалением необходимо учитывать свойство Загрузка: при записи
данных средствами механизма распределенной информационной базы данное
свойство будет установлено в значение Истина. В указанных процедурах
в режиме Загрузка не следует выполнять различные проверки, связанные
с наличием тех или иных данных, участвующих в обмене (например, регистратор записываемого набора записей регистра накопления может быть еще
не прочитан из сообщения обмена), не рекомендуется также выполнять изменение связанных данных.
В данном режиме рекомендуется дать возможность системе записать
прочитанные данные. Контроль допустимости данных для данного
узла распределенной информационной базы может быть осуществлен
в соответствующих обработчиках, вызываемых при чтении сообщения
обмена (ПриПолученииДанныхОтПодчиненного, ПриПолученииДанныхОтГлавного).

Особое внимание следует уделить регистрам сведений, имеющим независимый режим записи (свойство Режим записи установлено в значение
Независимый). Для данных регистров гранулой обмена является набор
записей с отбором по измерениям, с установленным свойством Основной

264

Технологии интеграции «1С:Предприятия 8.3»

отбор. Необходимо проанализировать, будет ли соответствовать состав
передаваемой информации логике работы конфигурации.
Например, в регистре сведений хранится информация о характеристиках
товаров. В основной отбор включены измерения:
■■ Характеристика (идентифицирующее характеристику товара);
■■ Товар (определяющее собственно товар).
По логике работы конфигурации важно обеспечить целостность редактирования характеристик товара − состав характеристик товара должен
редактироваться целиком. Считаем, что ситуация, когда в одном узле исправляется одна характеристика, а в другом – другая и через некоторое время
(после обмена данными) в характеристиках происходит объединениеисправлений, является недопустимой.
В данном случае (когда все измерения регистра сведений входят в основной
отбор) гранулой обмена данными выступает одна запись регистра сведений.
И это может привести к ситуации, при которой состав характеристик товара
будет состоять из различных характеристик, полученных из разных узлов
распределенной информационной базы (мы можем считать эту ситуацию
недопустимой).
Для устранения подобных проблем следует внести коррективы в набор
измерений, входящих в основной отбор регистра: исключить из основного
отбора измерение, вносящее излишнюю гранулярность (характеристику из
приведенного примера). Это позволит в процессе обмена передавать наборы
записей, содержащие информацию, которая полностью соответствует логике
работы конфигурации (все характеристики номенклатурной позиции являются логической единицей обмена).
Выполнение этих рекомендаций позволяет просто настроить обмен данными
в рамках распределенной информационной базы. Дальнейшая адаптация
конфигурации зависит от конкретной специфики обмена.

Пример реализации обмена данными
в распределенной информационной базе
В этом разделе мы протестируем обмен данными сначала интерактивным,
а затем программным образом, а также рассмотрим различные сценарии
обмена данными в распределенной информационной базе.
ПОДРОБНЕЕ
Подробнее познакомиться с реализацией обмена данными в распределенной
информационной базе можно в демонстрационной конфигурации
«Примеры обмена», которая прилагается к книге.

Глава 3. Обмен данными

265

В нашей демонстрационной конфигурации существует план обмена Магазины, на основе которого выполняется обмен данными в распределенной
информационной базе. Для этого у плана обмена установлен флажок Распределенная информационная база (рис. 3.12).

Рис. 3.12. План обмена «Магазины»

Состав плана обмена включает все объекты конфигурации за исключением
константы ПрефиксНомеров, которая служит для установки префиксов кодов/
номеров объектов, созданных в каждом узле обмена. Подробнее об этом
рассказывается в разделе «Подготовка конфигурации к работе в распределенной информационной базе».

266

Технологии интеграции «1С:Предприятия 8.3»

У плана обмена Магазины также существует реквизит Склад (тип СправочникСсылка.Склады) для хранения ссылки на склад, к которому относится
конкретный узел обмена. Подробнее об этом будет рассказано ниже, в разделе
«Распределение данных по подчиненным узлам».
Сначала создадим общий неглобальный модуль Обмен и поместим в нем
экспортную функцию ПолучитьПрефиксНомера() – листинг 3.23.
Листинг 3.23. Функция «ПолучитьПрефиксНомера»
Функция ПолучитьПрефиксНомера() Экспорт
Возврат Константы.ПрефиксНомеров.Получить();
КонецФункции

После этого в модули объектов всех справочников, участвующих в обмене,
поместим процедуру ПриУстановкеНовогоКода() – листинг 3.24.
Листинг 3.24. Процедура «ПриУстановкеНовогоКода»
Процедура ПриУстановкеНовогоКода(СтандартнаяОбработка, Префикс)
Префикс = Обмен.ПолучитьПрефиксНомера();
КонецПроцедуры

А в модули объектов всех документов, участвующих в обмене, поместим
процедуру ПриУстановкеНовогоНомера() – листинг 3.25.
Листинг 3.25. Процедура «ПриУстановкеНовогоНомера»
Процедура ПриУстановкеНовогоНомера(СтандартнаяОбработка, Префикс)
Префикс = Обмен.ПолучитьПрефиксНомера();
КонецПроцедуры

В результате созданные в базе каждого узла обмена объекты будут иметь
префиксы, соответствующие значению константы, установленному при
настройке этого узла.
Запустим «1С:Предприятие», введем некоторые тестовые данные и протестируем работу обмена в распределенной информационной базе.
В списке узлов плана обмена Магазины уже существует одна предопределенная запись, соответствующая нашей информационной базе. Это и будет
центральный узел плана обмена. Установим его код (ЦентрОфис) и название
(Центральный офис).

Глава 3. Обмен данными

267

Затем добавим в список еще одну запись с кодом Опт и наименованием
Оптовый. В поле Склад выберем значение Оптовый склад. Выделим этот узел,
нажмем кнопку Создать начальный образ… и создадим подчиненный узел
обмена путем создания начального образа из нашей центральной базы, как
описано в разделе «Создание начального образа» (рис. 3.13).

Рис. 3.13. Создание подчиненного узла «Оптовый» путем создания начального образа

При этом в базе подчиненного узла, в списке плана обмена Магазины, узел,
для которого создавалась база (с кодом Опт), будет внесен платформой как
предопределенный (помечен специальной пиктограммой в списке узлов),
а узел центральной базы будет добавлен в качестве главного узла обмена
(помечен специальной пиктограммой в списке узлов), рис. 3.14.

Рис. 3.14. Список узлов плана обмена для базы подчиненного узла

Для предотвращения конфликта записи объектов с одинаковыми номерами в
базе каждого из узлов обмена нужно установить значение константы Префикс
номеров. Подробнее об этом рассказывается в разделе «Подготовка конфигурации к работе в распределенной информационной базе». Для центральной
базы установим значение этой константы как «ЦО» (рис. 3.15).

268

Технологии интеграции «1С:Предприятия 8.3»

Рис. 3.15. Установка значения константы «ПрефиксНомеров»

После этого в базе центрального узла введем новый элемент справочника
Номенклатура. Код этого элемента будет иметь префикс «ЦО» (рис. 3.16).

Рис. 3.16. Ввод новых данных в центральной базе

Теперь нам нужно передать эти изменения подчиненному узлу Оптовый.
Для этого в центральной базе, в списке узлов плана обмена нужно выбрать
подчиненный узел, которому будут передаваться данные (Оптовый), и нажать
кнопку Записать изменения (рис. 3.17).

Рис. 3.17. Запись измененных данных из центральной базы
в подчиненный узел «Оптовый»

Откроем базу подчиненного узла Оптовый. Там мы увидим все данные, перенесенные из центральной базы при создании начального образа. В списке
узлов плана обмена Магазины выберем узел, из которого данные были выгружены (Центральный офис), нажмем кнопку Прочитать изменения и укажем
файл архива, в который были записаны изменения от центрального узла

Глава 3. Обмен данными

269

с автоматически сформированным именем Message_ЦентральныйОфис_
Оптовый (рис. 3.18).

Рис. 3.18. Чтение измененных данных из центральной базы в подчиненный узел

Произойдет загрузка данных из центральной базы, являющейся корневым
узлом, в подчиненный узел обмена. В справочнике Номенклатура мы увидим
еще одну запись, код которой начинается с префикса «ЦО» (рис. 3.19).

Рис. 3.19. Список справочника «Номенклатура» в подчиненном узле обмена «Оптовый»

Теперь установим значение константы Префикс номеров как «ОП». Добавим
нового контрагента. Его код будет начинаться с префикса «ОП». В списке
узлов плана обмена Магазины выберем узел, которому будут передаваться
данные (Центральный офис) и нажмем кнопку Записать изменения. После
этого откроем центральную базу, в списке узлов плана обмена Магазины
выберем узел, из которого данные были выгружены (Оптовый), нажмем
кнопку Прочитать изменения и укажем файл архива, в который были записаны
изменения от подчиненного узла с автоматически сформированным именем
Message_Оптовый_ЦентральныйОфис.

Таким образом, изменения данных можно производить в любом из узлов
плана обмена и обмениваться данными в обе стороны. Но, в отличие
от универсального обмена данными, в распределенных информационных
базах обмениваться данными можно только между парами узлов «главный –

270

Технологии интеграции «1С:Предприятия 8.3»

подчиненный», а не между подчиненными узлами. Изменения конфигурации
возможны только в корневом узле и должны быть переданы по всем уровням
иерархии.
Если изменить и конфигурацию, и данные и записать эти изменения, то они
будут переданы в одном сообщении обмена. В этом случае прочитать сообщение обмена в подчиненном узле нужно дважды. Сначала будут получены
изменения конфигурации. Затем нужно в режиме Конфигуратор обновить
конфигурацию и выполнить чтение изменений еще раз. После этого будут
получены измененные данные.

Сценарии обмена данными
в распределенной информационной базе
Обработчики событий ПриОтправкеДанныхПодчиненному, ПриОтправкеДанныхГлавному,
ПриПолученииДанныхОтПодчиненного,
ПриПолученииДанныхОтГлавного в модуле плана обмена позволяют
достаточно гибко управлять обменом данными в распределенной информационной базе. С использованием этих обработчиков может быть построено
большое разнообразие сценариев обмена данными. В этом разделе в качестве
примера будет рассмотрена организация нескольких сценариев.
Поведение по умолчанию
Данный сценарий является наиболее простым и соответствует поведению
распределенной информационной базы по умолчанию. Для этого сценария
характерно следующее:
■■ каждое изменение элемента данных, произведенное в любом из узлов
распределенной информационной базы, стремится распространиться
по всем узлам;
■■ разрешение коллизий производится на основании отношения узлов
«главный – подчиненный».
Для реализации такого сценария все обработчики не должны изменять
значения переданных им параметров, или же обработчики могут быть
не определены вовсе.
Распределение данных по подчиненным узлам
Данный сценарий подразумевает, что для некоторых элементов данных, для
которых он реализуется, выполняется следующее:
■■ вся совокупность элементов данных присутствует в главном узле;
■■ присутствие того или иного элемента данных в том или ином подчиненном узле определяется на основе сравнения значений некоторых

Глава 3. Обмен данными

271

реквизитов элемента данных с реквизитами подчиненного узла плана
обмена;
■■ разрешение коллизий производится на основании отношения узлов
«главный – подчиненный».
Для реализации данного сценария нужно обеспечить, чтобы при записи сообщения обмена данными в главном узле в сообщение не попадали элементы
данных, которые не должны присутствовать в подчиненном узле.
Кроме того, если значения реквизитов элемента данных могут быть изменены в подчиненном узле, то необходимо обеспечить, чтобы при получении
сообщения обмена данными в главном узле производилась регистрация изменений для тех объектов, которых в соответствии со значениями их реквизитов
в подчиненном узле быть не должно.
Для более детального рассмотрения примера предположим, что в качестве
типа элементов данных, для которых реализуется сценарий, выступает документ РасходнаяНакладная. У данного документа имеется реквизит Склад типа
СправочникСсылка.Склады. Обмен данными организован в соответствии
с планом обмена Магазины. У этого плана обмена также определен реквизит
Склад типа СправочникСсылка.Склады. В соответствии с этим планом
обмена организована распределенная информационная база, в которой
корневым узлом является центральный офис, а его подчиненными узлами –
склады. У каждого из подчиненных узлов плана обмена значение реквизита
Склад установлено так, чтобы обозначать, какому складу соответствует этот
узел. Все документы РасходнаяНакладная должны присутствовать в корневом
узле, а условием присутствия документов в подчиненных узлах является
равенство значений реквизитов Склад в документе и узле плана обмена.
В этом случае, для того чтобы документы РасходнаяНакладная не попадали
в те подчиненные узлы, куда они попадать не должны, обработчик события
ПриОтправкеДанныхПодчиненному в модуле плана обмена должен
иметь следующий вид (листинг 3.26).
Листинг 3.26. Процедура «ПриОтправкеДанныхПодчиненному()»
Процедура ПриОтправкеДанныхПодчиненному(ЭлементДанных, ОтправкаЭлемента)
ТипДанных = ТипЗнч(ЭлементДанных);
Если ТипДанных = Тип(″ДокументОбъект.РасходнаяНакладная″) Тогда

Если ЭлементДанных.Склад Склад Тогда

ОтправкаЭлемента = ОтправкаЭлементаДанных.Удалить;

КонецЕсли;
КонецЕсли;
КонецПроцедуры

272

Технологии интеграции «1С:Предприятия 8.3»

В приведенном примере обработчика анализируется тип элемента данных,
и если он равен ДокументОбъект.РасходнаяНакладная, то значение
реквизита Склад документа сравнивается со значением реквизита Склад
узла плана обмена. Если значения реквизитов равны, то значение параметра ОтправкаЭлемента можно не изменять (при вызове параметр имеет
значение Авто). При этом в сообщение будет помещено XML-представление
документа. Если же значения реквизитов не равны, то параметру ОтправкаЭлемента присваивается значение Удалить. В этом случае в сообщение
будет помещено XML-представление объекта УдалениеОбъекта,
проинициализированного ссылкой на соответствующий документ РасходнаяНакладная.
Может показаться странным, что в случае неравенства значений реквизитов
Склад параметру ОтправкаЭлемента присваивается значение Удалить, а
не Игнорировать, так как в случае значения Удалить XML-представление
объекта УдалениеОбъекта будет помещаться в сообщения, отправляемые
всем подчиненным узлам, кроме того узла, в который будет отправлен сам
документ. Таким образом, в значительной части случаев УдалениеОбъекта будет отправлено тем узлам, где документа, который требуется удалить,
никогда не было. Это действительно так, но в данном примере рассмотрен
наиболее общий случай. Если же, например, известно, что значение реквизита Склад документа РасходнаяНакладная может быть установлено только
при создании документа и в дальнейшем не может быть изменено, то параметру ОтправкаЭлемента в данном обработчике действительно могло бы
быть присвоено значение Игнорировать.
Если же значение реквизита Склад документа РасходнаяНакладная может
быть изменено в подчиненном узле, то в модуле плана обмена необходимо
определить обработчик события ПриПолученииДанныхОтПодчиненного
следующего вида (листинг 3.27).
Листинг 3.27. Процедура «ПриПолученииДанныхОтПодчиненного()»
Процедура ПриПолученииДанныхОтПодчиненного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад)
ТипДанных = ТипЗнч(ЭлементДанных);
Если ТипДанных = Тип(″ДокументОбъект.РасходнаяНакладная″) Тогда

Если ЭлементДанных.Склад Склад Тогда

ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать;

ОтправкаНазад = Истина;

КонецЕсли;
КонецЕсли;
КонецПроцедуры

Глава 3. Обмен данными

273

В приведенном примере обработчика анализируется тип элемента данных,
и если он равен ДокументОбъект.РасходнаяНакладная, то значение
реквизита Склад документа сравнивается со значением реквизита Склад узла
плана обмена. Если значения реквизитов равны, то значения параметров
ПолучениеЭлемента и ОтправкаНазад можно не изменять, обеспечив
этим поведение по умолчанию при приеме элемента данных. Если же
значения реквизитов не равны, то параметру ОтправкаНазад присваивается значение Истина. Тем самым гарантируется, что изменения документа
будут зарегистрированы и при отправке сообщения подчиненному узлу будет
отправлено УдалениеОбъекта, если, конечно, реквизит Склад документа не
будет изменен в главном узле так, что он окажется равен значению реквизита
Склад соответствующего узла плана обмена.
Если же значение реквизита Склад документа РасходнаяНакладная не может
быть изменено после создания документа, то обработчик ПриПолученииДанныхОтПодчиненного можно не определять.
Разрешение коллизий
На основе отношения «главный – подчиненный» в распределенной информационной базе организована типовая процедура разрешения коллизий,
автоматически выполняемая при приеме сообщения. Считается, что изменение элемента данных, произведенное в главном узле, имеет высший
приоритет по отношению к изменению, произведенному в подчиненном узле.
Таким образом, если сообщение, пришедшее от подчиненного узла, содержит
элемент данных, изменения которого зарегистрированы в базе главного узла
для этого подчиненного узла, то никаких действий предпринято не будет, то
есть этот элемент данных не будет помещен в базу данных и запись регистрации изменений не будет удалена.
Если сообщение, пришедшее от главного узла, содержит элемент данных,
изменения которого зарегистрированы в базе подчиненного узла для главного
узла, то элемент данных будет записан в базу данных, а запись регистрация
изменения будет удалена.
В случае если данный сценарий не устраивает, можно реализовать прямо
противоположный сценарий – при котором, например, принимаются изменения из нижестоящей базы.
Данный сценарий подразумевает, что для некоторых элементов данных, для
которых он реализуется, выполняется следующее:
■■ каждое изменение элемента данных, произведенное в любом из узлов
распределенной информационной базы, стремится распространиться
по всем узлам;

274

Технологии интеграции «1С:Предприятия 8.3»

■■ разрешение коллизий производится на основании отношения узлов
«главный – подчиненный», но более высокий приоритет имеет подчиненный узел.
Для рассмотрения данного случая воспользуемся приведенным выше
примером с документом РасходнаяНакладная и планом обмена Магазины.
В данном случае требуется определить обработчики событий ПриПолученииДанныхОтПодчиненного и ПриПолученииДанныхОтГлавного
в модуле плана обмена. Обработчик ПриПолученииДанныхОтПодчиненного будет иметь следующий вид (листинг 3.28).
Листинг 3.28. Процедура «ПриПолученииДанныхОтПодчиненного()»
Процедура ПриПолученииДанныхОтПодчиненного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад)
ТипДанных = ТипЗнч(ЭлементДанных);
Если ТипДанных = Тип(″ДокументОбъект.РасходнаяНакладная″) Тогда

ПолучениеЭлемента = ПолучениеЭлементаДанных.Принять;
КонецЕсли;
КонецПроцедуры

Приведенный обработчик весьма прост: проверяется тип элемента данных,
и если элемент данных относится к интересующему нас типу, то параметру
ПолучениеЭлемента присваивается значение Принять, что приводит
к безусловному приему элемента данных, независимо от того, зарегистрированы его изменения или нет.
Обработчик события ПриПолученииДанныхОтГлавного выглядит следующим образом (листинг 3.29).
Листинг 3.29. Процедура «ПриПолученииДанныхОтГлавного()»
Процедура ПриПолученииДанныхОтГлавного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад)
ТипДанных = ТипЗнч(ЭлементДанных);
Если ТипДанных = Тип(″ДокументОбъект.РасходнаяНакладная″) Тогда

Если ПланыОбмена.ИзменениеЗарегистрировано(Ссылка, ЭлементДанных) Тогда

ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать;

КонецЕсли
КонецЕсли;
КонецПроцедуры

Этот обработчик несколько сложнее. Если элемент данных относится
к интересующему нас типу, то производится проверка – зарегистрированы
ли изменения элемента данных для узла-отправителя сообщения. Если
изменения зарегистрированы, то параметру ПолучениеЭлемента присваивается значение Игнорировать. В результате прочитанный элемент данных

Глава 3. Обмен данными

275

не записывается в базу данных, а регистрация изменений сохраняется, что
позволит поместить элемент данных в сообщение, отправляемое главному
узлу.

Доработка примера обмена данными
в распределенной информационной базе
В соответствии с вышесказанным доработаем наш пример.
Во-первых, до сих пор мы никак не отслеживали факт соответствия значения
реквизита Склад передаваемых/принимаемых данных значению реквизита Склад узла обмена. Поэтому при получении данных в подчиненном
узле Оптовый (в нашем примере – путем создания начального образа) из
центральной базы туда попали данные, относящиеся розничному складу
(например, приходные накладные), хотя по условию нашей задачи этих
данных там быть не должно (рис. 3.20).

Рис. 3.20. Данные, полученные в узле обмена «Оптовый»

Во-вторых, мы реализуем обработку для выполнения программного обмена
данными через установленный каталог обмена.
А также покажем нестандартный вариант разрешения коллизий, при котором
изменения, сделанные в подчиненном узле, будут иметь более высокий приоритет над главным.

276

Технологии интеграции «1С:Предприятия 8.3»

Распределение данных по подчиненным узлам
Чтобы устранить первую проблему несоответствия складов, поместим
в модуле плана обмена Магазины обработчики событий ПриПолученииДанныхОтПодчиненного и ПриОтправкеДанныхПодчиненному.

Обработчик ПриОтправкеДанныхПодчиненному заполним следующим
образом (листинг 3.30).
Листинг 3.30. Процедура «ПриОтправкеДанныхПодчиненному»
Процедура ПриОтправкеДанныхПодчиненному(ЭлементДанных, ОтправкаЭлемента)
// Определить Склад, которому принадлежат отправляемые данные (если он есть).
СкладДанных = ОпределитьСклад(ЭлементДанных);
// Отправить данные, у которых Склад соответствует складу подчиненного узла плана обмена.
Если СкладДанных Неопределено Тогда


Если СкладДанных Склад Тогда


ОтправкаЭлемента = ОтправкаЭлементаДанных.Удалить;


КонецЕсли;

КонецЕсли;
КонецПроцедуры

Это обработчик был подробно рассмотрен выше в листинге 3.26, за исключением функции ОпределитьСклад() для определения склада элемента
данных обмена, который содержится в параметре ЭлементДанных. Код
функции будет приведен в листинге 3.32.
Обработчик события ПриПолученииДанныхОтПодчиненного заполним
следующим образом (листинг 3.31).
Листинг 3.31. Процедура «ПриПолученииДанныхОтПодчиненного»
Процедура ПриПолученииДанныхОтПодчиненного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад)
// Определить Склад, которому принадлежат получаемые данные.
СкладДанных = ОпределитьСклад(ЭлементДанных);
// Получить данные, у которых Склад соответствует складу подчиненного узла плана обмена.
Если СкладДанных Неопределено Тогда


Если СкладДанных Склад И Склад Справочники.Склады.ПустаяСсылка() Тогда


ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать;

ОтправкаНазад = Истина;


КонецЕсли;

КонецЕсли;
КонецПроцедуры

Глава 3. Обмен данными

277

Это обработчик был подробно рассмотрен выше в листинге 3.27, поэтому мы
не будем здесь еще раз это повторять.
Кроме того, поместим в модуле плана обмена функцию ОпределитьСклад(), которая будет возвращать ссылку на склад переданного элемента
данных или возвращать Неопределено, если реквизит Склад у элемента
данных отсутствует (листинг 3.32).
Листинг 3.32. Функция «ОпределитьСклад»
Функция ОпределитьСклад(ЭлементДанных)
СкладДанных = Неопределено;
Если ТипЗнч(ЭлементДанных) = Тип(″СправочникОбъект.Склады″) Тогда


СкладДанных = ЭлементДанных.Ссылка;

ИначеЕсли ТипЗнч(ЭлементДанных) = Тип(″ДокументОбъект.ПриходнаяНакладная″)

Или ТипЗнч(ЭлементДанных) = Тип(″ДокументОбъект.РасходнаяНакладная″) Тогда


СкладДанных = ЭлементДанных.Склад;

ИначеЕсли ТипЗнч(ЭлементДанных) = Тип(″РегистрНакопленияНаборЗаписей.УчетНоменклатуры″) Тогда


// Определить склад набора записей регистра накопления

// УчетНоменклатуры по значению склада первой записи регистра.

Если ЭлементДанных.Количество() > 0 Тогда


СкладДанных = ЭлементДанных[0].Склад;


КонецЕсли;

КонецЕсли;
Возврат СкладДанных;
КонецФункции

Кроме ссылки на реквизит Склад у документов ПриходнаяНакладная, РасходнаяНакладная и ссылки на измерение Склад у набора записей регистра
накопления УчетНоменклатуры функция также возвращает ссылку на сам
элемент справочника Склады, так как в узле обмена должен присутствовать
только «свой» элемент справочника.
Теперь создадим еще раз начальный образ центральной базы для узла обмена
Оптовый, как описано в разделе «Создание начального образа». При переносе
элементов данных в начальный образ для каждого элемента данных вызывается обработчик события ПриОтправкеДанныхПодчиненному, поэтому
данные, не соответствующие оптовому складу, не будут перенесены.

278

Технологии интеграции «1С:Предприятия 8.3»

В результате в базу подчиненного узла помимо «общих» (не имеющих
привязки к складу) данных будут перенесены только те данные, склад
которых совпадает со складом узла обмена (рис. 3.21).

Рис. 3.21. Данные, перенесенные в узел обмена «Оптовый»

Если же в подчиненном узле ввести, например, расходную накладную
с «неправильным» складом, то при получении изменений в главном узле
в соответствии с обработчиком события ПриПолученииДанныхОтПодчиненного этот документ будет проигнорирован. То есть в базу данных он
записан не будет, но изменения этого документа будут зарегистрированы для
подчиненного узла. И при следующей отправке данных этому узлу в соответствии с обработчиком события ПриОтправкеДанныхПодчиненному ему
будет послано удаление «неправильного» документа.

Глава 3. Обмен данными

279

Обработка для выполнения обмена
Все описанные выше действия по обмену данными в распределенной информационной базе можно выполнить также программно. Покажем это на
примере специальной обработки, с помощью которой для выбранного узла
будут реализовываться те же самые действия, которые ранее мы выполняли
интерактивно.
Итак, в конфигураторе центральной базы добавим обработку ОбменСМагазинами и откроем ее основную форму.
Добавим в форму реквизит Магазин типа ПланОбменаСсылка.Магазины, в котором будет содержаться ссылка на выбранный узел обмена.
А также добавим в форму команды: ЗаписатьИзменения, ПрочитатьИзменения и СоздатьНачальныйОбраз. Перетащим эти команды и реквизит в окно
элементов формы.
У всех трех кнопок, соответствующих командам, снимем флажок у свойства
Доступность. Это делается для того, чтобы действия по созданию начального
образа, записи и чтению изменений обмена становились возможны только
в случае выбора в поле Магазин непредопределенного узла, который не совпадает с узлом текущей информационной базы.
Для того чтобы обеспечить такое поведение кнопок, поместим в модуле
формы функцию ПолучитьДоступностьПоУзлу(), в которую передается
ссылка на выбранный узел (листинг 3.33).
Листинг 3.33. Функция «ПолучитьДоступностьПоУзлу»
&НаСервереБезКонтекста
Функция ПолучитьДоступностьПоУзлу(Узел)
ДоступностьКнопок = Ложь;
// Определить наличие установленного непредопределенного узла.
Если Узел ПланыОбмена.Магазины.ПустаяСсылка()
И Узел ПланыОбмена.Магазины.ЭтотУзел()
И Узел.ПолучитьОбъект() Неопределено Тогда


ДоступностьКнопок = Истина;

КонецЕсли;

Возврат ДоступностьКнопок;
КонецФункции

Эта функция будет возвращать Истина, в случае если ссылка, содержащаяся
в параметре Узел, не пустая и не равна ссылке на предопределенный узел,
соответствующий текущей информационной базе. Для получения ссылки
на текущий узел используется метод менеджера узла плана обмена
ЭтотУзел(). В противном случае функция вернет Ложь.

280

Технологии интеграции «1С:Предприятия 8.3»

После этого создадим обработчик события ПриИзменении для поля формы
Магазин, в котором доступность кнопок будет зависеть от значения, возвращенного описанной ранее функцией (листинг 3.34).
Листинг 3.34. Обработчик события «ПриИзменении» поля «Магазин»
&НаКлиенте
Процедура МагазинПриИзменении(Элемент)
ДоступностьКнопок = ПолучитьДоступностьПоУзлу(Магазин);
// Установить доступность кнопок в зависимости от установленного узла обмена.
Элементы.СоздатьНачальныйОбраз.Доступность = ДоступностьКнопок;
Элементы.ЗаписатьИзменения.Доступность = ДоступностьКнопок;
Элементы.ПрочитатьИзменения.Доступность = ДоступностьКнопок;
КонецПроцедуры

Теперь создадим обработчик команды СоздатьНачальныйОбраз и заполним
следующим образом (листинг 3.35).
Листинг 3.35. Обработчик команды «СоздатьНачальныйОбраз»
&НаКлиенте
Процедура СоздатьНачальныйОбраз(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.ВыборКаталога);
Диалог.Заголовок = ″Выбор каталога для создания начального образа″;
Диалог.Показать(Новый ОписаниеОповещения(″ПослеВыбораКаталога″, ЭтотОбъект));
КонецПроцедуры

В этом обработчике мы вызываем диалог выбора каталога, в который будет
помещен начальный образ информационной базы, и показываем его пользователю с помощью немодального метода Показать(). В этот метод мы
передаем описание оповещения, указывающее на экспортную процедуру
ПослеВыбораКаталога(), которая будет вызвана после того, как пользователь выберет каталог (листинг 3.36).
Листинг 3.36. Процедура «ПослеВыбораКаталога»
&НаКлиенте
Процедура ПослеВыбораКаталога(ВыбранныеФайлы, Параметры) Экспорт
Если ВыбранныеФайлы = Неопределено Тогда

Возврат;
КонецЕсли;
Каталог = ВыбранныеФайлы[0];
СоздатьНачальныйОбразНаСервере(Магазин, Каталог);

Глава 3. Обмен данными

281

Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Создание начального образа узла завершено.″;
Сообщение.Сообщить();
КонецПроцедуры

В этом обработчике оповещения, в случае если каталог выбран, мы получаем
путь к каталогу как первый элемент массива, содержащийся в параметре
ВыбранныеФайлы, и вызываем процедуру СоздатьНачальныйОбразНаСервере(), в которую передаем этот каталог и выбранный узел обмена,
содержащийся в реквизите Магазин (листинг 3.37).
Листинг 3.37. Процедура «СоздатьНачальныйОбразНаСервере»
&НаСервереБезКонтекста
Процедура СоздатьНачальныйОбразНаСервере(Узел, КаталогСоединения)
ПланыОбмена.СоздатьНачальныйОбраз(Узел, ″File =″″″ + КаталогСоединения + ″″″″);
КонецПроцедуры

В этой процедуре вызывается метод СоздатьНачальныйОбраз() объекта
ПланыОбменаМенджер, с помощью которого и создается начальный образ
для подчиненного узла распределенной информационной базы. В первом
параметре мы передаем в этот метод ссылку на узел, для которого мы хотим
создать начальный образ, а во втором – строку соединения с создаваемой
информационной базой.
Теперь создадим обработчик команды ЗаписатьИзменения и заполним следующим образом (листинг 3.38).
Листинг 3.38. Обработчик команды «ЗаписатьИзменения»
&НаКлиенте
Процедура ЗаписатьИзменения(Команда)
ЗаписатьИзмененияНаСервере(Магазин);
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Запись изменений завершена.″;
Сообщение.Сообщить();
КонецПроцедуры

В этом обработчике мы вызываем процедуру ЗаписатьИзмененияНаСервере(). В эту процедуру мы передаем выбранный узел обмена,
содержащийся в реквизите Магазин (листинг 3.39), и выводим сообщение
пользователю об успешной записи изменений, зарегистрированных для этого
узла.

282

Технологии интеграции «1С:Предприятия 8.3»

Листинг 3.39. Процедура «ЗаписатьИзмененияНаСервере»
&НаСервереБезКонтекста
Процедура ЗаписатьИзмененияНаСервере(Узел)
// Сформировать полное имя файла обмена.
Каталог = Константы.КаталогОбмена.Получить();
ИмяФайла = Каталог + ?(Прав(Каталог, 1) = ″\″,″″, ″\″) + ″Message_″

+ СокрЛП(ПланыОбмена.Магазины.ЭтотУзел().Код) + ″_″ +

СокрЛП(Узел.Код) + ″.xml″;

// Создать и проинициализировать объект ЗаписьXML.
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяФайла);
// Создать объект ЗаписьСообщенияОбмена и начать запись сообщения.
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, Узел);
// Записать содержимое тела сообщения обмена данными распределенной ИБ.
ПланыОбмена.ЗаписатьИзменения(ЗаписьСообщения);
// Закончить запись сообщения и запись XML.
ЗаписьСообщения.ЗакончитьЗапись();
ЗаписьXML.Закрыть();
КонецПроцедуры

В этой процедуре сначала мы формируем имя файла, в который будет записано сообщение обмена.
Для упрощения примера мы будем обмениваться сообщениями через
заранее известный каталог обмена, путь к которому хранится в константе
Каталог обмена. Значение этой константы (например, e:\Exchange) наряду
с префиксом номеров можно задать в форме настроек информационной базы
(см. рис. 3.15).
Имена файлов сообщений стандартизованы и имеют вид Message _
_ .xml. В качестве
узла-отправителя сообщения обмена здесь выступает предопределенный
узел информационной базы, полученный с помощью метода ЭтотУзел(),
а в качестве узла-получателя – узел, ссылка на который содержится в параметре Узел.

Для доступа к файлу обмена мы создаем объект ЗаписьXML. Затем
с помощью метода СоздатьЗаписьСообщения() объекта ПланыОбменаМенджер создаем объект ЗаписьСообщенияОбмена, с помощью которого будет выполняться запись сообщений обмена данными. При вызове
метода этого объекта НачатьЗапись() во втором параметре мы указываем,
для какого узла будет производиться выгрузка изменений.

Глава 3. Обмен данными

283

После этого мы вызываем метод ЗаписатьИзменения() объекта ПланыОбменаМенджер, который и записывает изменения, предназначенные для
передачи в выбранный узел, в сообщение обмена с ранее сформированным
именем.
В заключение мы заканчиваем запись сообщения обмена и закрываем файл.
Теперь создадим обработчик команды ПрочитатьИзменения и заполним следующим образом (листинг 3.40).
Листинг 3.40. Обработчик команды «ПрочитатьИзменения»
&НаКлиенте
Процедура ПрочитатьИзменения(Команда)
ПрочитатьИзмененияНаСервере(Магазин);
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″Чтение изменений завершено.″;
Сообщение.Сообщить();
КонецПроцедуры

В этом обработчике мы вызываем процедуру ПрочитатьИзмененияНаСервере(). В эту процедуру мы передаем выбранный узел обмена,
содержащийся в реквизите Магазин (листинг 3.41), и выводим сообщение
пользователю об успешном получении изменений, от этого узла.
Листинг 3.41. Процедура «ПрочитатьИзмененияНаСервере»
&НаСервереБезКонтекста
Процедура ПрочитатьИзмененияНаСервере(Узел)
// Сформировать полное имя файла обмена.
Каталог = Константы.КаталогОбмена.Получить();
ИмяФайла = Каталог + ?(Прав(Каталог, 1) = ″\″,″″, ″\″) + ″Message_″ + СокрЛП(Узел.Код) + ″_″ +

СокрЛП(ПланыОбмена.Магазины.ЭтотУзел().Код) + ″.xml″;

// Создать и проинициализировать объект ЧтениеXML.
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайла);

// Создать объект ЧтениеСообщенияОбмена и начать чтение сообщения.
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
// Прочитать содержимое тела сообщения.
ПланыОбмена.ПрочитатьИзменения(ЧтениеСообщения);
// Закончить чтение сообщения и чтение XML.
ЧтениеСообщения.ЗакончитьЧтение();
ЧтениеXML.Закрыть();
КонецПроцедуры

284

Технологии интеграции «1С:Предприятия 8.3»

В этой процедуре мы формируем имя файла, из которого будет прочитано сообщение обмена. Это делается по аналогии с процедурой
ЗаписатьИзмененияНаСервере(), приведенной в листинге 3.39. Только
здесь узел-отправитель и узел-получатель сообщения меняются местами.
Для доступа к файлу обмена мы создаем объект ЧтениеXML. Затем
с помощью метода СоздатьЧтениеСообщения() объекта ПланыОбменаМенджер создаем объект ЧтениеСообщенияОбмена, с помощью которого
выполняется чтение сообщений обмена данными.
После этого мы вызываем метод ПрочитатьИзменения() объекта ПланыОбменаМенджер, в котором и происходит чтение изменений, полученных от
выбранного узла, из сообщения обмена с ранее сформированным именем.
В заключение мы заканчиваем чтение сообщения обмена и закрываем файл.
Проверим, как это работает. Запустим «1С:Предприятие» и в форме настроек
информационной базы зададим путь к каталогу обмена (e:\Exchange),
который будет храниться в константе Каталог обмена (см. рис. 3.15).
Затем откроем список узлов плана обмена Магазины. Добавим в этот список
еще одну запись с кодом Розн и наименованием Розничный. В поле Склад
выберем значение Розничный склад. Затем откроем обработку Обмен с магазинами, в поле Магазин выберем узел обмена Розничный и нажмем кнопку
Создать начальный образ (рис. 3.22).

Рис. 3.22. Программное создание начального образа для узла «Розничный»

Таким образом, мы программно создадим подчиненный узел обмена путем
создания начального образа из нашей центральной базы.

Глава 3. Обмен данными

285

Откроем базу подчиненного узла Розничный. Там мы увидим все данные,
перенесенные из центральной базы при создании начального образа.
При программном обмене также будет поддерживаться стратегия распространения данных по подчиненным узлам, описанная ранее в разделе
«Распределение данных по подчиненным узлам». В результате в базу розничного узла помимо «общих» данных (не привязанных к складу) попадут только
те данные, склад которых совпадает со складом узла обмена. Поскольку такое
поведение реализовано в модуле плана обмена Магазины, то оно «работает»
независимо от способа обмена данными.
Теперь установим значение константы Префикс номеров как «РЗ» и зададим
путь к каталогу обмена (e:\Exchange). Добавим новую номенклатуру. Ее
код будет начинаться с префикса «РЗ». После этого откроем обработку Обмен
с магазинами, в поле Магазин выберем узел, которому будут передаваться
данные (Центральный офис), и нажмем кнопку Записать изменения (рис. 3.23).

Рис. 3.23. Запись изменений в подчиненном узле для центральной базы

286

Технологии интеграции «1С:Предприятия 8.3»

Чтобы получить эти изменения, в центральной базе откроем обработку Обмен
с магазинами, в поле Магазин выберем узел, из которого данные были выгружены (Розничный), и нажмем кнопку Прочитать изменения. В результате новая
номенклатура, добавленная в подчиненном узле, появится в центральной
базе.
При этом при записи изменений для центральной базы в каталоге обмена
появится файл сообщения обмена с именем Message_Розн_ЦентрОфис.xml.
Из этого файла изменения от розничного узла будут прочитаны
в центральную базу. Выбор файла сообщения пользователем не понадобится, поскольку имена файлов стандартизованы, а каталог обмена хранится
в константе информационной базы.
Разрешение коллизий
До сих пор мы обменивались только вновь создаваемыми данными, поэтому
у нас не возникало ситуаций, когда одни и те же данные изменялись бы сразу
несколькими участниками обмена. На самом деле вполне возможна ситуация, когда, например, в один и тот же существующий документ вносятся
изменения и в центральной базе, и в подчиненном узле. Такая ситуация называется «коллизией».
В результате, если ничего не предпринимать, после обмена данными в документе останутся только те изменения, которые были внесены в центральной
базе. Изменения документа, сделанные в подчиненном узле, будут проигнорированы.
Это стандартный вариант разрешения коллизий, основанный на том, что
главный узел обмена (в нашем примере это центральная база) имеет более
высокий приоритет (его изменения более важны), чем подчиненный. Такой
сценарий обмена данными в распределенной информационной базе заложен
изначально и не требует никакого программирования.
Но бывают случаи, когда требуется реализовать какой-то особенный
сценарий обмена. Например, вся нормативно-справочная информация
ведется в центральной базе, а расходные и приходные накладные составляются в подчиненных узлах.
В этом случае коллизия должна быть разрешена ровно наоборот – когда
изменения подчиненного узла имеют более высокий приоритет над главным.
Но это касается только документов и их движений в регистрах. Обмен всеми
остальными объектами происходит «по правилам».
Для решения данной задачи поместим в модуле плана обмена Магазины
обработчик события ПриПолученииДанныхОтГлавного и дополним
обработчик ПриПолученииДанныхОтПодчиненного.

Глава 3. Обмен данными

287

Эти обработчики были подробно рассмотрены выше в листингах 3.28, 3.29,
за исключением функции ПринятьИзменения() для определения типа
элемента данных обмена, который содержится в параметре ЭлементДанных. Эта функция будет возвращать Истина, в случае если элемент
данных имеет тип расходной или приходной накладных или связанных
с ними регистров. В противном случае функция вернет Ложь. Поместим эту
функцию в модуле плана обмена и заполним следующим образом (листинг
3.42).
Листинг 3.42. Функция «ПринятьИзменения»
Функция ПринятьИзменения(ЭлементДанных)
Прием = Ложь;
ТипДанных = ТипЗнч(ЭлементДанных);
Если ТипЗнч(ЭлементДанных) = Тип(″ДокументОбъект.ПриходнаяНакладная″)

Или ТипЗнч(ЭлементДанных) = Тип(″ДокументОбъект.РасходнаяНакладная″)

Или ТипЗнч(ЭлементДанных) = Тип(″РегистрНакопленияНаборЗаписей.УчетНоменклатуры″) Тогда


Прием = Истина;

КонецЕсли;
Возврат Прием;
КонецФункции

Обработчик ПриПолученииДанныхОтПодчиненного дополним следующим фрагментом, выделенным жирным шрифтом (листинг 3.43).
Листинг 3.43. Процедура «ПриПолученииДанныхОтПодчиненного()»
Процедура ПриПолученииДанныхОтПодчиненного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад)
Если ПринятьИзменения(ЭлементДанных) Тогда
ПолучениеЭлемента = ПолучениеЭлементаДанных.Принять;
КонецЕсли;

// Определить Склад, которому принадлежат получаемые данные.
СкладДанных = ОпределитьСклад(ЭлементДанных);
// Получить данные, у которых Склад соответствует складу подчиненного узла плана обмена.

КонецПроцедуры

288

Технологии интеграции «1С:Предприятия 8.3»

В этом обработчике определяется, что от подчиненного узла будут безусловно приниматься все изменения интересующих нас документов и их
движений. Для остальных элементов обмена никаких действий предпринято
не будет, что равносильно стандартному варианту разрешения коллизий.
Обработчик события ПриПолученииДанныхОтГлавного заполним следующим образом (листинг 3.44).
Листинг 3.44. Процедура «ПриПолученииДанныхОтГлавного()»
Процедура ПриПолученииДанныхОтГлавного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад)
Если ПланыОбмена.ИзменениеЗарегистрировано(Ссылка, ЭлементДанных)

И ПринятьИзменения(ЭлементДанных) Тогда


ПолучениеЭлемента =ПолучениеЭлементаДанных.Игнорировать;

КонецЕсли;
КонецПроцедуры

В этом обработчике определяется, что если в базе подчиненного узла зарегистрированы изменения интересующих нас документов и их движений для
главного узла, то при получении данных от главного узла соответствующие
изменения будут проигнорированы. В результате прочитанный элемент
данных не записывается в базу подчиненного узла, а регистрация изменений
сохраняется, что позволит поместить этот элемент данных в сообщение,
отправляемое главному узлу.
Убедимся, что все работает правильно для всех узлов обмена, причем
и программным, и интерактивным способом. Поскольку такой вариант
разрешения коллизий реализован в модуле плана обмена Магазины, он будет
«работать» независимо от способа обмена данными.

Особенности использования последовательности
документов в распределенной информационной базе
Для отслеживания правильного порядка проведения документов в «1С:Предприятии» служит механизм последовательностей документов. Этот механизм позволяет отслеживать порядок проведения документов и производить
восстановление этого порядка. Для того чтобы документ оказался в последовательности, он должен зарегистрироваться в последовательности, тогда
механизм последовательностей будет учитывать его при своей работе.
При организации работы последовательности документов в распределенной информационной базе нужно учитывать, что участие документа
в последовательности имеет смысл только в одном узле распределенной

Глава 3. Обмен данными

289

информационной базы. Это может быть либо узел, в котором документ был
создан, либо другой узел, но узел должен быть один. Нарушение данного
принципа может привести к различным проблемам в процессе работы с
системой – например, невозможности восстановления последовательности
документов.
Таким образом, документ, участвующий в последовательности, должен регистрироваться в последовательности только в одном узле информационной
базы. Для этого документ должен содержать информацию, по которой на
момент записи документа можно сделать вывод, должен он регистрироваться
в данном узле или нет. Для того чтобы документ мог это определить, все узлы
информационной базы должны иметь уникальную идентификацию.
Например, документ может содержать реквизит, который содержит код узла
плана обмена его информационной базы. Коды узлов информационных баз
должны быть в этом случае уникальными. Этого можно добиться организационными методами. Основываясь на информации о принадлежности
данному узлу распределенной информационной базы, документ должен
при записи либо очистить набор записей регистрации в последовательности, либо, наоборот, его заполнить. Тем самым будет достигнута цель
регистрации документа в последовательности только в собственном узле
информационной базы.
Пример очистки наборов записей в последовательностях (фрагмент кода
размещается в обработке проведения документа) приведен в листинге 3.45.
Листинг 3.45. Пример очистки набора записей регистрации документа
в последовательности
Если УзелПланыОбмена.Расходные.ЭтотУзел().Код Тогда
Для Каждого НаборПоследовательности Из ПринадлежностьПоследовательностям Цикл

НаборПоследовательности.Очистить();
КонецЦикла;
КонецЕсли;

Узел − реквизит документа, содержащий код узла, в котором он должен реги-

стрироваться в последовательностях.

Как уже было сказано, документ может участвовать в последовательности
только в одном узле информационной базы. Поэтому сами последовательности документов не должны участвовать в обмене данных. Иначе записи
регистрации документа будут переданы в другой узел информационной базы,
тем самым нарушив принцип регистрации документа в последовательности
только в одном узле информационной базы.

290

Технологии интеграции «1С:Предприятия 8.3»

Универсальный механизм обмена данными
Универсальный механизм обмена данными может использоваться как вместе
с механизмом распределенных информационных баз, так и по отдельности
(для решения задач организации обмена данными информационных баз
«1С:Предприятия 8» с различными программными системами).
В качестве программных систем, с которыми может быть организован
универсальный обмен данными, могут выступать другие информационные
базы «1С:Предприятия», в том числе с отличающимися конфигурациями,
совершенно другие программные комплексы (в этом и состоит отличие
от механизма распределенных информационных баз, в рамках которого организуется обмен между базами данных с идентичными конфигурациями).
К универсальному механизму обмена данными могут быть отнесены:
■■ средства чтения и записи документов XML,
■■ XML-сериализация,
■■ планы обмена.
Функциональность планов обмена была рассмотрена в предыдущих разделах
данной главы (см. раздел «Планы обмена»). Возможности базовых средств
чтения и записи XML-документов подробно рассматриваются в шестой
главе, в разделе «XML-файлы».

Использование возможностей работы
с XML-документами
До данного момента рассматривались только механизмы регистрации
и удаления регистрации изменений. Рассмотрим пример кода, реализующий
(в рамках универсального обмена) полный цикл выгрузки данных (включает
в себя выборку данных, для которых были зарегистрированы изменения).
При этом в качестве файлов «носителей данных» будут использоваться документы XML.
Следует отметить, что обмен данными может выполняться через файлы
других форматов, но записать/прочитать данные с помощью объектов
ЧтениеСообщенияОбмена и ЗаписьСообщенияОбмена без указания
в качестве параметра объектов читающих и записывающих XML нельзя
(листинг 3.46).

Глава 3. Обмен данными

291

Листинг 3.46. Пример выгрузки данных
ЗаписьXML = Новый ЗаписьXML()
ЗаписьXML.ОткрытьФайл(ИмяФайлаСообщения);
Узел = ПланыОбмена.УдаленныеСклады.НайтиПоКоду(КодУзла);
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, Узел);
Выборка = ПланыОбмена.ВыбратьИзменения(Узел, ЗаписьСообщения.НомерСообщения);
Пока Выборка.Следующий() Цикл
Данные = Выборка.Получить();
ЗаписатьXML(ЗаписьXML, Данные);
КонецЦикла;
ЗаписьСообщения.ЗакончитьЗапись();

Для получения выборки данных, для которых зарегистрированы изменения,
используется метод ВыбратьИзменения(). Для обхода полученных данных
используется метод Следующий() выборки. В процессе выборки изменений
в записи таблиц регистрации изменений (которые раньше не выгружались
и у которых, соответственно, в поле таблицы НомерСообщения находится
значение Null) проставляется номер сообщения обмена данными, в котором
должны передаваться изменения.
Представленный алгоритм выгрузки предполагает, что отправленные
сообщения могут быть потеряны по тем или иным причинам. Если потерь
сообщения не может быть в принципе (гарантированная доставка), то в конце
алгоритма можно поместить следующую строку (листинг 3.47).
Листинг 3.47. Удаление регистрации изменений при гарантированной доставке
ПланыОбмена.УдалитьРегистрациюИзменений(Узел, НомерСообщения);

В этом случае из таблицы регистрации изменений будут удалены записи,
относящиеся к узлу, ссылка на который передана в качестве первого параметра метода. Причем будут удалены не все записи, а только те, в которых
номер сообщения равен или меньше того значения, которое передано
в качестве второго параметра метода. Это те записи, в которых была произведена первая отправка изменений.
Фрагмент кода, в котором производится чтение сообщения, содержащего
измененные данные, выглядит следующим образом (листинг 3.48).

292

Технологии интеграции «1С:Предприятия 8.3»

Листинг 3.48. Пример чтения сообщения обмена
ЧтениеXML = Новый ЧтениеXML()
ЧтениеXML.ОткрытьФайл(ИмяФайлаСообщения);
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
ПланыОбмена.УдалитьРегистрациюИзменений(ЧтениеСообщения.Отправитель

, ЧтениеСообщения.НомерПринятого);
Пока ВозможностьЧтенияXML(ЧтениеXML) Цикл
Данные = ПрочитатьXML(ЧтениеXML);
Данные.ОбменДанными.Отправитель = ЧтениеСообщения.Отправитель;
Данные.ОбменДанными.Загрузка = Истина;
Данные.Записать();
КонецЦикла;
ЧтениеСообщения.ЗакончитьЧтение();

В приведенных выше примерах чтения и записи сообщений не учитывалось,
что при обмене данными может случиться так, что один и тот же элемент
данных будет изменен одновременно в двух обменивающихся данными
узлах. В этом случае непонятно, какое из изменений должно быть в конечном
счете принято. Такая ситуация называется коллизией.
Один из способов разрешить коллизию – определить, какой из узлов является
главным, а какой – подчиненным. При этом должно быть принято изменение,
сделанное в главном узле, а изменение, сделанное в подчиненном узле,
должно быть отвергнуто.
Для реализации этого при приеме сообщения перед записью данных
необходимо установить, зарегистрировано ли изменение этих данных,
и в зависимости от роли узла в данной паре «получатель – отправитель»
принять решение: записывать или не записывать данные.
Ниже (листинг 3.49) приведен пример реализации стратегии «главный –
подчиненный» при чтении сообщения. Предполагается, что для хранения
роли узла в плане обмена был определен реквизит Главный, имеющий тип
Булево.
Листинг 3.49. Пример реализации стратегии «главный – подчиненный»
ЧтениеXML = Новый ЧтениеXML()
ЧтениеXML.ОткрытьФайл(ИмяФайлаСообщения);
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
ПланыОбмена.УдалитьРегистрациюИзменений(ЧтениеСообщения.Отправитель

, ЧтениеСообщения.НомерПринятого);
Отправитель = ЧтениеСообщения.Отправитель;

Глава 3. Обмен данными

293

Главный = Отправитель.Главный;
Пока ВозможностьЧтенияXML(ЧтениеXML) Цикл
Данные = ПрочитатьXML(ЧтениеXML);
Если Главный ИЛИ (Не ПланыОбмена.ИзменениеЗарегистрировано(Отправитель, Данные)) Тогда

Данные.ОбменДанными.Отправитель = ЧтениеСообщения.Отправитель;

Данные.ОбменДанными.Загрузка = Истина;

Данные.Записать();
КонецЕсли;
КонецЦикла;
ЧтениеСообщения.ЗакончитьЧтение();

Следует обратить внимание на необходимость установки у загружаемого
элемента данных свойства Загрузка в значение Истина. В обработчиках
событий записи объекта рекомендуется отслеживать данный режим и не
делать каких-либо проверок на корректность заполнения, присутствие какихлибо связанных данных. Это обусловлено тем фактом, что на момент
загрузки данного объекта другие объекты могут быть еще не загружены.

Пример реализации универсального обмена
В этом примере мы не только реализуем обмен данными с помощью универсальных механизмов обмена стандартным образом, но и покажем, как
организовать обмен данными с различной структурой, как реализовать стратегию распространения данных по конкретным узлам, покажем возможность
разрешения коллизий, когда одни и те же данные изменяются одновременно
в нескольких узлах обмена. Кроме того, рассмотрим возможность ручной
регистрации изменений данных.
ПОДРОБНЕЕ
Подробнее познакомиться с реализацией универсального обмена данными
можно в демонстрационной конфигурации «Примеры обмена», которая
прилагается к книге.

В нашей демонстрационной конфигурации существует план обмена УдаленныеСклады. У данного плана обмена имеются следующие реквизиты:
■■ Склад (тип СправочникСсылка.Склады) – ссылка на склад, представляющий данный узел;
■■ Главный (тип Булево) – для реализации стратегии «главный – подчиненный».
Состав плана обмена представлен на рисунке (рис. 3.24).

294

Технологии интеграции «1С:Предприятия 8.3»

Рис. 3.24. Состав плана обмена «УдаленныеСклады»

Состав плана обмена включает все объекты конфигурации за исключением
константы ПрефиксНомеров, которая служит для установки префиксов кодов/
номеров объектов, созданных в каждом узле обмена. Подробнее об этом
рассказывается в разделе «Подготовка конфигурации к работе в распределенной информационной базе».
Итак, сначала мы создадим форму узла (форму объекта) нашего плана
обмена.
При создании нового узла обмена нам нужно указать, какой узел обмена
будет главным для текущей информационной базы. Забегая вперед, поясним,
что это свойство понадобится нам для разрешения коллизий при получении
данных. Кроме того, нам нужно выполнить для узла своего рода начальную
синхронизацию всех данных обмена.
Для этого добавим в форму команду ЗарегистрироватьИзменения и перетащим
ее в окно элементов формы. Однако кнопка, связанная с этой командой, а
также возможность установки флажка Главный должны быть недоступны для
предопределенного узла, соответствующего данной информационной базе.
Для этого создадим обработчик события формы ПриСозданииНаСервере
и заполним его следующим образом (листинг 3.50).

Глава 3. Обмен данными

295

Листинг 3.50. Обработчик события формы «ПриСозданииНаСервере»
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Объект.ЭтотУзел = Истина Тогда

Элементы.Главный.Доступность = Ложь;

Элементы.ФормаЗарегистрироватьИзменения = Ложь;
КонецЕсли;
КонецПроцедуры

В этом обработчике для определения предопределенного узла мы используем свойство узла плана обмена ЭтотУзел. Если оно принимает значение
Истина, значит, это узел, соответствующий нашей информационной базе.

Создадим обработчик команды ЗарегистрироватьИзменения, из которого будет
вызываться процедура ЗарегистрироватьИзмененияНаСервере().
В эту процедуру будет передаваться ссылка на узел обмена, для которого
нужно зарегистрировать изменения (листинг 3.51).
Листинг 3.51. Обработчик команды «ЗарегистрироватьИзменения»
&НаКлиенте
Процедура ЗарегистрироватьИзменения(Команда)
ЗарегистрироватьИзмененияНаСервере(Объект.Ссылка);
КонецПроцедуры

Процедуру ЗарегистрироватьИзмененияНаСервере() заполним следующим образом (листинг 3.52).
Листинг 3.52. Процедура «ЗарегистрироватьИзмененияНаСервере»
&НаСервереБезКонтекста
Процедура ЗарегистрироватьИзмененияНаСервере(Узел)
ПланыОбмена.ЗарегистрироватьИзменения(Узел);
КонецПроцедуры

В этой процедуре мы обращаемся к механизму регистрации изменений,
вызывая метод менеджера планов обмена – ЗарегистрироватьИзменения(). В этот метод передается ссылка на текущий узел плана обмена.
В результате выполнения этой процедуры в информационной базе будут
созданы записи регистрации изменений, предназначенные для пересылки
в указанный узел, для всех объектов обмена, входящих в состав нашего плана
обмена.

296

Технологии интеграции «1С:Предприятия 8.3»

Стандартные процедуры обмена
Для выполнения обмена данными добавим в конфигурацию обработку
ОбменСУдаленнымиСкладами и откроем ее основную форму.
Добавим в форму реквизит УзелОбмена типа ПланОбменаСсылка.
УдаленныеСклады, в котором будет содержаться ссылка на выбранный
узел обмена. А также добавим в форму команды ВыгрузитьДанные и ЗагрузитьДанные. Перетащим эти команды и реквизит в окно элементов формы.
У всех кнопок, соответствующих командам, снимем флажок у свойства
Доступность. Это делается для того, чтобы действия по выполнению обмена
становились возможны только в случае выбора в поле Узел обмена непредопределенного узла, который не совпадает с узлом текущей информационной
базы.
Для того чтобы обеспечить такое поведение кнопок, поместим в модуле
формы функцию ПолучитьДоступностьПоУзлу(), в которую передается
ссылка на выбранный узел (листинг 3.53).
Листинг 3.53. Функция «ПолучитьДоступностьПоУзлу»
&НаСервереБезКонтекста
Функция ПолучитьДоступностьПоУзлу(Узел)
ДоступностьКнопок = Ложь;
// Определить наличие установленного непредопределенного узла.
Если Узел ПланыОбмена.УдаленныеСклады.ПустаяСсылка()
И Узел ПланыОбмена.УдаленныеСклады.ЭтотУзел()
И Узел.ПолучитьОбъект() Неопределено Тогда

ДоступностьКнопок = Истина;

КонецЕсли;

Возврат ДоступностьКнопок;
КонецФункции

Эта функция будет возвращать Истина, в случае если ссылка, содержащаяся
в параметре Узел, не пустая и не равна ссылке на предопределенный узел,
соответствующий текущей информационной базе. Для получения ссылки на
текущий узел используется метод менеджера узла плана обмена ЭтотУзел().
В противном случае функция вернет Ложь.
После этого создадим обработчик события ПриИзменении для поля формы
УзелОбмена, в котором доступность кнопок будет зависеть от значения,
возвращенного описанной ранее функцией (листинг 3.54).

Глава 3. Обмен данными

297

Листинг 3.54. Обработчик события «ПриИзменении» поля «УзелОбмена»
&НаКлиенте
Процедура УзелОбменаПриИзменении(Элемент)
ДоступностьКнопок = ПолучитьДоступностьПоУзлу(УзелОбмена);
// Установить доступность кнопок в зависимости от установленного узла обмена.
Элементы.ЗагрузитьДанные.Доступность = ДоступностьКнопок;
Элементы.ВыгрузитьДанные.Доступность = ДоступностьКнопок;
КонецПроцедуры

Теперь создадим обработчик команды ВыгрузитьДанные и заполним следующим образом (листинг 3.55).
Листинг 3.55. Обработчик команды «ВыгрузитьДанные»
&НаКлиенте
Процедура ВыполнитьВыгрузку(Команда)
ВыполнитьВыгрузкуНаСервере(УзелОбмена);
КонецПроцедуры

В этом обработчике мы вызываем процедуру ВыполнитьВыгрузкуНаСервере(), в которую передаем ссылку на узел обмена, выбранный в поле
обработки. В реквизите УзелОбмена будет содержаться ссылка на узел, для
которого будет производиться запись изменений (листинг 3.56).
Листинг 3.56. Процедура «ВыполнитьВыгрузкуНаСервере»
&НаСервереБезКонтекста
Процедура ВыполнитьВыгрузкуНаСервере(Узел)
// Получить объект узла обмена.
УзелОбмена = Узел.ПолучитьОбъект();
// Записать новое сообщение обмена.
УзелОбмена.ЗаписатьСообщениеСИзменениями();
КонецПроцедуры

В этой процедуре мы получаем объект от ссылки на узел плана обмена
и выполняем процедуру ЗаписатьСообщенияСИзменениями(), которую
мы поместим в модуле плана обмена УдаленныеСклады (см. листинг 3.59).
Теперь создадим обработчик команды ЗагрузитьДанные и заполним следующим образом (листинг 3.57).

298

Технологии интеграции «1С:Предприятия 8.3»

Листинг 3.57. Обработчик команды «ЗагрузитьДанные»
&НаКлиенте
Процедура ВыполнитьЗагрузку(Команда)
ВыполнитьЗагрузкуНаСервере(УзелОбмена);
КонецПроцедуры

В этом обработчике мы вызываем процедуру ВыполнитьЗагрузкуНаСервере(), в которую передаем ссылку на узел обмена, выбранный
в поле обработки. В реквизите УзелОбмена будет содержаться ссылка на узел,
от которого будет производиться чтение изменений (листинг 3.58).
Листинг 3.58. Процедура «ВыполнитьЗагрузкуНаСервере»
&НаСервереБезКонтекста
Процедура ВыполнитьЗагрузкуНаСервере(Узел)
// Получить объект узла обмена.
УзелОбмена = Узел.ПолучитьОбъект();
// Прочитать новое сообщение обмена.
УзелОбмена.ПрочитатьСообщениеСИзменениями();
КонецПроцедуры

В этой процедуре мы получаем объект от ссылки на узел плана обмена
и выполняем процедуру ПрочитатьСообщенияСИзменениями(), которую
мы поместим в модуле плана обмена УдаленныеСклады (см. листинг 3.61).
Процедуру ЗаписатьСообщенияСИзменениями() в модуле плана обмена
заполним следующим образом (листинг 3.59).
Листинг 3.59. Процедура для записи данных обмена
Процедура ЗаписатьСообщениеСИзменениями() Экспорт
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″-------- Выгрузка в узел ″ + Строка(ЭтотОбъект) + ″ ------------″;
Сообщение.Сообщить();
// Получить имя файла обмена.
ИмяФайла = ОбменСУдаленнымиСкладами.ПолучитьИмяФайлаОбмена(

ПланыОбмена.УдаленныеСклады.ЭтотУзел(), Ссылка);
// Создать объект записи XML.
// *** ЗаписьXML-документов.
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяФайла);
ЗаписьXML.ЗаписатьОбъявлениеXML();

Глава 3. Обмен данными

299

// *** Инфраструктура сообщений.
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, Ссылка);
// Записать соответствие пространств имен для сокращения размера файла сообщения
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен(″xsd″, ″http://www.w3.org/2001/XMLSchema″);
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен(″xsi″, ″http://www.w3.org/2001/XMLSchema-instance″);
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен(″v8″, ″http://v8.1c.ru/data″);
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″ Номер сообщения: ″ + ЗаписьСообщения.НомерСообщения;
Сообщение.Сообщить();
// Получить выборку измененных данных.
// *** Механизм регистрации изменений.
ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(

ЗаписьСообщения.Получатель, ЗаписьСообщения.НомерСообщения);
Пока ВыборкаИзменений.Следующий() Цикл

// Записать данные в сообщение *** XML-сериализация.

ЗаписатьXML(ЗаписьXML, ВыборкаИзменений.Получить());
КонецЦикла;
ЗаписьСообщения.ЗакончитьЗапись();
ЗаписьXML.Закрыть();
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″-------- Конец выгрузки ------------″;
Сообщение.Сообщить();
КонецПроцедуры

В этой процедуре сначала мы сообщаем пользователю о начале выгрузки
данных в узел обмена.
Затем с помощью функции ПолучитьИмяФайлаОбмена(), расположенной
в общем модуле ОбменСУдаленнымиСкладами, мы формируем имя файла,
в который будет записано сообщение обмена.
Для упрощения примера мы будем обмениваться сообщениями через
заранее известный каталог обмена, путь к которому хранится в константе
Каталог обмена. Значение этой константы (например, e:\Exchange), наряду
с префиксом номеров, можно задать в форме настроек информационной базы
(см. рис. 3.15).
Имена файлов сообщений стандартизованы и имеют вид: Message__.xml. В качестве узла-отправителя
сообщения обмена в функцию ПолучитьИмяФайлаОбмена() передается ссылка на предопределенный узел информационной базы, полученный
с помощью метода ЭтотУзел(), а в качестве узла-получателя – ссылка на тот
узел обмена, в модуле которого мы находимся (листинг 3.60).

300

Технологии интеграции «1С:Предприятия 8.3»

Листинг 3.60. Функция «ПолучитьИмяФайлаОбмена()»
Функция ПолучитьИмяФайлаОбмена(УзелИсточник, УзелПриемник) Экспорт
// Сформировать полное имя файла обмена.
Каталог = Константы.КаталогОбмена.Получить();
ИмяФайла = Каталог + ?(Прав(Каталог, 1) = ″\″,″″, ″\″) + ″Message_″ +

СокрЛП(УзелИсточник.Код) + ″_″ + СокрЛП(УзелПриемник.Код) + ″.xml″;
Возврат ИмяФайла;
КонецФункции

После этого мы обращаемся к механизмам записи XML-документов
и создаем новый объект – ЗаписьXML. С помощью него открываем для
записи XML-файл с полученным ранее именем и записываем в него объявление XML.
Затем мы обращаемся к механизмам инфраструктуры сообщений и создаем
новый объект ЗаписьСообщенияОбмена, метод которого НачатьЗапись() позволяет, кроме всего прочего, создать очередной номер сообщения
и записать заголовок сообщения в XML.
В качестве ссылки на узел обмена, который является получателем сообщения,
мы используем значение стандартного реквизита Ссылка плана обмена
УдаленныеСклады, в модуле которого мы находимся.
Для сокращения размера файла сообщения первоначально определяем соответствие используемым пространствам имен.
После этого, чтобы получить данные, которые необходимо сохранить в этом
файле, мы обращаемся к механизму регистрации изменений и получаем
выборку из записей регистрации изменений, предназначенных узлу-получателю. При формировании выборки методом менеджера планов обмена
ВыбратьИзменения() мы передаем вторым параметром номер сообщения.
В цикле перебора измененных данных мы сериализуем эти данные
в открытый XML-файл методом глобального контекста ЗаписатьXML.
После выхода из цикла мы заканчиваем запись методом ЗакончитьЗапись()
объекта ЗаписьСообщенияОбмена. Затем закрываем файл
с сообщением обмена методом Закрыть() объекта ЗаписьXML и выводим
сообщение об окончании выгрузки данных.
Процедуру ПрочитатьСообщенияСИзменениями() в модуле плана
обмена заполним следующим образом (листинг 3.61).

Глава 3. Обмен данными
Листинг 3.61. Процедура для чтения данных обмена
Процедура ПрочитатьСообщениеСИзменениями() Экспорт
// Получить имя файла обмена.
ИмяФайла = ОбменСУдаленнымиСкладами.ПолучитьИмяФайлаОбмена(

Ссылка, ПланыОбмена.УдаленныеСклады.ЭтотУзел());
Файл = Новый Файл(ИмяФайла);
Если Не Файл.Существует() Тогда

Возврат;
КонецЕсли;
// Попытаться открыть файл.
// *** Чтение документов XML.
ЧтениеXML = Новый ЧтениеXML;
Попытка

ЧтениеXML.ОткрытьФайл(ИмяФайла);
Исключение

Сообщение = Новый СообщениеПользователю;

Сообщение.Текст = ″Невозможно открыть файл обмена данными.″;

Сообщение.Сообщить();


Возврат;

КонецПопытки;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″-------- Загрузка из ″ + Строка(ЭтотОбъект) + ″ ------------″;
Сообщение.Сообщить();
Сообщение.Текст = ″ – Считывается файл ″ + ИмяФайла;
Сообщение.Сообщить();
// Загрузить данные из найденного файла.
// *** Инфраструктура сообщений.
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
// Прочитать заголовок сообщения обмена данными – файла XML.
ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
// Проверить, что сообщение предназначено для этого узла.
Если ЧтениеСообщения.Отправитель Ссылка Тогда

ВызватьИсключение ″Неверный узел″;
КонецЕсли;
// Удалить регистрацию изменений для узла-отправителя сообщения.
// *** Служба регистрации изменений.
ПланыОбмена.УдалитьРегистрациюИзменений(

ЧтениеСообщения.Отправитель, ЧтениеСообщения.НомерПринятого);
// Прочитать данные из сообщения в цикле. *** XML-сериализация.
Пока ВозможностьЧтенияXML(ЧтениеXML) Цикл


// Прочитать очередное значение.

301

302

Технологии интеграции «1С:Предприятия 8.3»


Данные = ПрочитатьXML(ЧтениеXML);


// Записать полученные данные.

Данные.ОбменДанными.Отправитель = ЧтениеСообщения.Отправитель;

Данные.ОбменДанными.Загрузка = Истина;

Данные.Записать();

КонецЦикла;
ЧтениеСообщения.ЗакончитьЧтение();
ЧтениеXML.Закрыть();
УдалитьФайлы(ИмяФайла);
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ″-------- Конец загрузки ------------″;
Сообщение.Сообщить();
КонецПроцедуры

В этой процедуре с помощью функции ПолучитьИмяФайлаОбмена(),
расположенной в общем модуле ОбменСУдаленнымиСкладами (см. листинг
3.60), мы формируем имя файла, из которого будет прочитано сообщение
обмена.
Имена файлов сообщений стандартизованы и имеют вид: Message__.xml. В качестве узла-отправителя
сообщения обмена в функцию ПолучитьИмяФайлаОбмена() передается
ссылка на тот узел обмена, в модуле которого мы находимся, а в качестве
узла-получателя – ссылка на предопределенный узел информационной базы.
Затем на основе имени файла мы создаем объект Файл и проверяем, существует ли он. Если файл обмена найден, то мы пытаемся открыть его для
чтения с помощью объекта ЧтениеXML. В случае успеха выводим сообщение о начале загрузки данных из файла.
Затем мы обращаемся к механизмам инфраструктуры сообщений планов
обмена и создаем объект ЧтениеСообщенияОбмена. Используя метод
этого объекта НачатьЧтение(), мы считываем заголовок XML-сообщения,
в котором содержится, в том числе, информация об отправителе сообщения.
После этого проверяем, является ли отправитель сообщения тем узлом плана
обмена, для которого мы в данном вызове этой процедуры производим обмен
данными. В качестве ссылки на узел обмена, который является отправителем
сообщения, мы используем значение стандартного реквизита Ссылка плана
обмена УдаленныеСклады, в модуле которого мы находимся.
Если все в порядке, то, перед тем как начать чтение данных, мы удаляем
все записи регистрации изменений, которые были сделаны для этого узла
и соответствовали номерам сообщений, меньшим или равным указанному

Глава 3. Обмен данными

303

в обрабатываемом нами сообщении как номер принятого. Это делается затем,
чтобы исключить дублирование данных, которые уже были ранее посланы
этому узлу и им обработаны. Для этого мы обращаемся к службе регистрации
изменений менеджера планов обмена и используем метод УдалитьРегистрациюИзменений().

Затем в цикле мы выполняем чтение данных, содержащихся
в сообщении обмена. При этом для каждого элемента в функции
ВозможностьЧтенияXML() получается очередной тип данных XML
и определяется, имеется ли соответствующий тип «1С:Предприятия».
Для чтения данных мы используем метод глобального контекста
ПрочитатьXML(). В результате переменная Данные будет содержать объект
«1С:Предприятия», полученный из сообщения обмена.
После этого мы записываем полученные данные методом Записать().
Перед записью полученного объекта мы устанавливаем у него в параметрах
обмена данными узел отправителя, чтобы система при записи этого объекта
в нашей базе данных не формировала записи регистрации изменений этого
объекта для того узла, от которого мы его только что получили.
Кроме того, в параметрах обмена данными мы устанавливаем свойство
Загрузка, информирующее систему о том, что запись объекта будет происходить в режиме обновления данных, полученных в результате обмена. Такое
указание позволяет системе упростить процедуру записи объекта, отказавшись от ряда стандартных проверок и исключив изменения связанных
данных, которые выполняются при обычной записи.
После того как все сообщение обмена будет нами обработано, мы
заканчиваем чтение методом ЗакончитьЧтение() объекта ЧтениеСообщенияОбмена. Затем закрываем файл с сообщением обмена методом
Закрыть() объекта ЧтениеXML и удаляем этот файл из каталога обмена.
В заключение выводим сообщение об окончании загрузки данных из файла.
Стратегия распространения данных
В предыдущем разделе мы рассмотрели стандартные процедуры выгрузки/
загрузки данных в узлы обмена. Но на самом деле обычно их требуется
дополнить так, чтобы они решали еще ряд задач, возникающих при обмене
данными. Например, при передаче данных обмена в другие узлы требуется
реализовать стратегию распространения данных, при которой конкретным
узлам должны отправляться только данные, соответствующие определенному условию.

304

Технологии интеграции «1С:Предприятия 8.3»

В нашем примере мы реализуем стратегию распространения данных,
согласно которой документы ПриходнаяНакладная и РасходнаяНакладная
(и соответствующие им наборы записей регистра накопления УчетНоменклатуры) выгружаются только в том случае, если значение реквизита Склад
документов (и наборов записей регистра накопления) совпадает по значению
с одноименным реквизитом, определенным для узла-получателя.
Для этого дополним процедуру ЗаписатьСообщениеСИзменениями()
в модуле плана обмена (см. листинг 3.59) следующим образом (листинг 3.62).
Листинг 3.62. Процедура «ЗаписатьСообщениеСИзменениями»
Процедура ЗаписатьСообщениеСИзменениями() Экспорт

// Получить выборку измененных данных.
// *** Механизм регистрации изменений.
ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(

ЗаписьСообщения.Получатель, ЗаписьСообщения.НомерСообщения);
Пока ВыборкаИзменений.Следующий() Цикл
Данные = ВыборкаИзменений.Получить();

// Проверить, нужен ли перенос данных. Если нет, то записать удаление этих данных.
Если Не ОбменСУдаленнымиСкладами.НуженПереносДанных(
ЗаписьСообщения.Получатель, Данные) Тогда
ОбменСУдаленнымиСкладами.УдалениеДанных(Данные);
КонецЕсли;

// Записать данные в сообщение *** XML-сериализация.

ЗаписатьXML(ЗаписьXML, Данные);
КонецЦикла;

КонецПроцедуры

Добавленный фрагмент выделен жирным шрифтом.
В цикле перебора измененных данных, перед тем как записать данные
в XML-файл, мы проверяем, нужно ли выгружать эти данные в узел-получатель. Проверка выполняется в функции НуженПереносДанных(),
расположенной в общем модуле ОбменСУдаленнымиСкладами (см. листинг
3.63). В случае если выгрузка не должна производиться (функция возвращает
Ложь), вызывается процедура УдалениеДанных(), расположенная в общем
модуле ОбменСУдаленнымиСкладами (см. листинг 3.64).

Глава 3. Обмен данными

305

Листинг 3.63. Функция для реализации стратегии переноса данных
Функция НуженПереносДанных(Получатель, Данные) Экспорт
Перенос = Истина;
Склад = Получатель.Склад;
Если ТипЗнч(Данные) = Тип(″ДокументОбъект.ПриходнаяНакладная″)

ИЛИ ТипЗнч(Данные) = Тип(″ДокументОбъект.РасходнаяНакладная″) Тогда


// Проверить, совпадает ли склад документов со складом узла обмена.

Если Не Склад.Пустая() И Данные.Склад Склад Тогда

Перенос = Ложь;

КонецЕсли;
ИначеЕсли ТипЗнч(Данные) = Тип(″РегистрНакопленияНаборЗаписей.УчетНоменклатуры″) Тогда


// Проверить, что у всех записей набора склад один и тот же.

СкладНабора = Неопределено;

Для Каждого Запись Из Данные Цикл

Если СкладНабора Неопределено И Запись.Склад СкладНабора Тогда

ВызватьИсключение ″Склад должен быть одинаковый для всех записей″;

КонецЕсли;

СкладНабора = Запись.Склад;

КонецЦикла;


// Проверить, совпадает ли склад набора записей со складом узла обмена.

Если Не Склад.Пустая() И СкладНабора Склад Тогда

Перенос = Ложь;

КонецЕсли;
КонецЕсли;
Возврат Перенос;
КонецФункции

Функция НуженПереносДанных() реализует стратегию распространения
данных, описанную выше. В параметре Данные в функцию передается
полученный объект обмена, а в параметре Получатель – ссылка на узелполучатель сообщения.
Если тип объекта ДокументОбъект.ПриходнаяНакладная или ДокументОбъект.РасходнаяНакладная или РегистрНакопленияНаборЗаписей.УчетНоменклатуры, то проверяется следующее условие
переноса данных. Если реквизит Склад для узла-получателя заполнен и не
равен значению одноименного реквизита документов ПриходнаяНакладная
или РасходнаяНакладная или регистра накопления УчетНоменклатуры, то
функция возвращает Ложь. Во всех остальных случаях возвращается
Истина.

306

Технологии интеграции «1С:Предприятия 8.3»

Для набора записей регистра накопления производится проверка на то, что
во всех записях используется один склад. Данная проверка может и не проводиться, если такая ситуация изначально не может встретиться.
В случае если данные (полученные при выборке изменений) не подлежат
выгрузке в узел-получатель, производится вызов процедуры УдалениеДанных(). В этой процедуре для данных, имеющих объектную природу,
создается объект УдалениеОбъекта. Этим достигается удаление в узлеприемнике «ранее неправильно отосланных данных». Наборы записей
регистров очищаются (листинг 3.64).
Листинг 3.64. Функция для удаления данных при обмене
Процедура УдалениеДанных(Данные) Экспорт
// Получить объект описания метаданного, соответствующий данным.
ОбъектМетаданных = ?(ТипЗнч(Данные) = Тип(″УдалениеОбъекта″)

, Данные.Ссылка.Метаданные(), Данные.Метаданные());
Если Метаданные.Справочники.Содержит(ОбъектМетаданных)
ИЛИ Метаданные.Документы.Содержит(ОбъектМетаданных)

ИЛИ Метаданные.ПланыСчетов.Содержит(ОбъектМетаданных)

ИЛИ Метаданные.ПланыВидовРасчета.Содержит(ОбъектМетаданных)

ИЛИ Метаданные.ПланыВидовХарактеристик.Содержит(ОбъектМетаданных) Тогда


// Записать удаление объекта для объектных данных.

Данные = Новый УдалениеОбъекта(Данные.Ссылка);

ИначеЕсли Метаданные.РегистрыСведений.Содержит(ОбъектМетаданных)

ИЛИ Метаданные.РегистрыНакопления.Содержит(ОбъектМетаданных)

ИЛИ Метаданные.РегистрыБухгалтерии.Содержит(ОбъектМетаданных)

ИЛИ Метаданные.РегистрыРасчета.Содержит(ОбъектМетаданных)

ИЛИ Метаданные.РегистрыРасчета.Содержит(ОбъектМетаданных.Родитель)

ИЛИ Метаданные.Последовательности.Содержит(ОбъектМетаданных) Тогда


// Очистить данные.

Данные.Очистить();

КонецЕсли;
КонецПроцедуры

Обмен данными с разной структурой
Поскольку рассматриваемый нами механизм выгрузки относится к разряду
универсальных, подразумевается, что обмен данными производится между
конфигурациями, имеющими разную структуру.
Рассмотрим простой пример обмена данными с различной структурой.
Например, в базе узла-получателя в иерархическом справочнике Номенклатура присутствует дополнительный реквизит по сравнению с базой
узла-отправителя, и наоборот.

Глава 3. Обмен данными

307

В этом случае запись всех «общих» для обоих узлов реквизитов справочника
должна производиться в ручном режиме. Порядок следования реквизитов
справочника в конфигурации может не совпадать с порядком их записи
в XML, но последующее чтение реквизитов из XML должно производиться
в том же порядке, в каком они были записаны. Все остальные данные с идентичной структурой сериализуются в/из XML стандартным образом.
Для этого дополним процедуру ЗаписатьСообщениеСИзменениями()
в модуле плана обмена (см. листинг 3.59) следующим образом (листинг 3.65).
Листинг 3.65. Процедура «ЗаписатьСообщениеСИзменениями»
Процедура ЗаписатьСообщениеСИзменениями() Экспорт

// Получить выборку измененных данных.
// *** Механизм регистрации изменений.
ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(

ЗаписьСообщения.Получатель, ЗаписьСообщения.НомерСообщения);
Пока ВыборкаИзменений.Следующий() Цикл

Данные = ВыборкаИзменений.Получить();


// Проверить, нужен ли перенос данных. Если нет, то записать удаление этих данных.

Если Не ОбменСУдаленнымиСкладами.НуженПереносДанных(

ЗаписьСообщения.Получатель, Данные) Тогда

ОбменСУдаленнымиСкладами.УдалениеДанных(Данные);

КонецЕсли;
// Записать данные в сообщение *** XML-сериализация.
ОбменСУдаленнымиСкладами.ЗаписатьДанные(ЗаписьXML, Данные);
КонецЦикла;

КонецПроцедуры

Добавленный фрагмент выделен жирным шрифтом. В этом фрагменте
данные, полученные в цикле перебора записей регистрации изменений,
сериализуются в открытый XML-файл с помощью процедуры ЗаписатьДанные(), расположенной в общем модуле ОбменСУдаленнымиСкладами. В параметре Данные в процедуру передается полученный объект
обмена (листинг 3.66).
Листинг 3.66. Процедура «ЗаписатьДанные»
Процедура ЗаписатьДанные(ЗаписьXML, Данные) Экспорт
Удаление = ?(ТипЗнч(Данные) = Тип(″УдалениеОбъекта″), Истина, Ложь);
// Получить объект описания метаданного, соответствующий данным.
ОбъектМетаданных = ?(Удаление, Данные.Ссылка.Метаданные(), Данные.Метаданные());

308

Технологии интеграции «1С:Предприятия 8.3»

Если Не Удаление И ОбъектМетаданных = Метаданные.Справочники.Номенклатура Тогда

// Записать элемент справочника вручную.

ЗаписатьXMLНоменклатура(ЗаписьXML, Данные);
Иначе


// Записать данные с помощью стандартного метода.

ЗаписатьXML(ЗаписьXML, Данные);
КонецЕсли
КонецПроцедуры

В процедуре производится проверка: если записывается не удаление данных
и записываемый объект является элементом справочника Номенклатура, то
вызывается процедура ЗаписатьXMLНоменклатура(), расположенная
в общем модуле ОбменСУдаленнымиСкладами, для записи элемента справочника вручную (листинг 3.67). В противном случае запись производится
с помощью стандартного метода ЗаписатьXML().
Листинг 3.67. Процедура «ЗаписатьXMLНоменклатура»
Процедура ЗаписатьXMLНоменклатура(ЗаписьXML, Товар )Экспорт
// Записать начало элемента XML.
ЗаписьXML.ЗаписатьНачалоЭлемента(″CatalogObject.Номенклатура.Вручную″);
// Ссылка
ЗаписатьXML(ЗаписьXML, Товар.Ссылка, ″Ref″, НазначениеТипаXML.Явное);
// ЭтоГруппа
ЗаписатьXML(ЗаписьXML, Товар.ЭтоГруппа, ″IsFolder″, НазначениеТипаXML.Явное);
// Родитель
ЗаписатьXML(ЗаписьXML, Товар.Родитель, ″Parent″, НазначениеТипаXML.Явное);
// Наименование
ЗаписатьXML(ЗаписьXML, Товар.Наименование, ″Description″, НазначениеТипаXML.Явное);
// Код
ЗаписатьXML(ЗаписьXML, Товар.Код, ″Code″, НазначениеТипаXML.Явное);
// Записать реквизиты, выгружаемые только для элемента справочника.
Если Не Товар.ЭтоГруппа Тогда

// ЗакупочнаяЦена

ЗаписатьXML(ЗаписьXML, Товар.ЗакупочнаяЦена, ″ЗакупочнаяЦена″, НазначениеТипаXML.Явное);

// ФайлКартинки

ЗаписатьXML(ЗаписьXML, Товар.ФайлКартинки, ″ФайлКартинки″, НазначениеТипаXML.Явное);

// Картинка

ЗаписатьXML(ЗаписьXML, Товар.Картинка, ″Картинка″, НазначениеТипаXML.Явное);
КонецЕсли;
// Записать конец элемента.
ЗаписьXML.ЗаписатьКонецЭлемента();
КонецПроцедуры

Глава 3. Обмен данными

309

В процедуре ЗаписатьXMLНоменклатура() производится «ручное»
формирование элемента CatalogObject.Номенклатура.Вручную.

Сначала в открытый XML-файл мы записываем начало элемента с заголовком, указывающим на ручное формирование элемента. Затем при помощи
метода ЗаписатьXML() записываем значения «общих» реквизитов, которые
хотим передать с помощью обмена. Третьим параметром передаем строковое
имя записываемого реквизита и в качестве четвертого параметра указываем
значение перечисления НазначениеТипаXML, определяющее необходимость явного указания типа данных XML. После этого мы завершаем запись
элемента и записываем в XML-файл конец элемента.
В процедуру ПрочитатьСообщенияСИзменениями() в модуле плана
обмена (см. листинг 3.61) внесем следующие изменения (листинг 3.68).
Листинг 3.68. Процедура «ПрочитатьСообщениеСИзменениями»
Процедура ПрочитатьСообщениеСИзменениями() Экспорт

// Прочитать данные из сообщения в цикле. *** XML-сериализация.
Пока ОбменСУдаленнымиСкладами.ВозможностьЧтенияДанных(ЧтениеXML) Цикл

// Прочитать очередное значение.
Данные = ОбменСУдаленнымиСкладами.ПрочитатьДанные(ЧтениеXML);

// Записать полученные данные.

Данные.ОбменДанными.Отправитель = ЧтениеСообщения.Отправитель;

Данные.ОбменДанными.Загрузка = Истина;

Данные.Записать();

КонецЦикла;

КонецПроцедуры

Добавленный фрагмент выделен жирным шрифтом. В этом фрагменте в цикле
мы выполняем чтение данных, содержащихся в сообщении обмена. При этом
для каждого элемента в функцииВозможностьЧтенияДанных(), расположенной в общем модуле ОбменСУдаленнымиСкладами (см. листинг 3.69),
проверяется возможность чтения данных. Если имя типа – CatalogObject.
Товары.Вручную, возвращается значение Истина. В противном случае
вызывается стандартный метод ВозможностьЧтенияXML(), с помощью
которого из объекта ЧтениеXML получается очередной тип данных XML
и определяется, имеется ли соответствующий тип «1С:Предприятия».

310

Технологии интеграции «1С:Предприятия 8.3»

Листинг 3.69. Функция «ВозможностьЧтенияДанных»
Функция ВозможностьЧтенияДанных(ЧтениеXML)
// Получить тип данных XML, который может быть считан в данный момент.
ТипXML = ПолучитьXMLТип(ЧтениеXML);
Если ТипXML = Неопределено Тогда

Возврат Ложь;
КонецЕсли;
Если ТипXML.ИмяТипа = ″CatalogObject.Номенклатура.Вручную″ И ТипXML.URIПространстваИмен = ″″

Тогда

Возврат Истина;
КонецЕсли;
Возврат ВозможностьЧтенияXML(ЧтениеXML);
КонецФункции

В цикле чтения данных в процедуре ПрочитатьСообщениеСИзменениями() вызывается функция ПрочитатьДанные(), расположенная
в общем модуле ОбменСУдаленнымиСкладами (см. листинг 3.70), в которой
производится чтение данных из элемента. При этом если читается элемент
CatalogObject.Номенклатура.Вручную, то вызывается функция
ПрочитатьXMLНоменклатура(), расположенная в общем модуле
ОбменСУдаленнымиСкладами (см. листинг 3.71) для «ручного» чтения
данных, в противном случае чтение осуществляется с помощью стандартного метода ПрочитатьXML.
Листинг 3.70. Функция «ПрочитатьДанные»
Функция ПрочитатьДанные(ЧтениеXML)
ТипXML = ПолучитьXMLТип(ЧтениеXML);
Если ТипXML = Неопределено Тогда

Возврат Неопределено;
КонецЕсли;
Если ТипXML.ИмяТипа = ″CatalogObject.Номенклатура.Вручную″ И ТипXML.URIПространстваИмен = ″″

Тогда

// Прочитать значение справочника Номенклатура.

Возврат ПрочитатьXMLНоменклатура(ЧтениеXML);
КонецЕсли;
// Прочитать значение из объекта ЧтениеXML стандартным образом.
Возврат ПрочитатьXML(ЧтениеXML);
КонецФункции

Глава 3. Обмен данными
Листинг 3.71. Функция «ПрочитатьXMLНоменклатура»
Функция ПрочитатьXMLНоменклатура(ЧтениеXML)Экспорт
Если ЧтениеXML.ТипУзла ТипУзлаXML.НачалоЭлемента Тогда

ВызватьИсключение ″Ошибка чтения XML″;
КонецЕсли;
// Прочитать следующий узел XML-документа.
ЧтениеXML.Прочитать();
// Прочитать ссылку на элемент справочника Номенклатура.
ТоварСсылка = ПрочитатьXML(ЧтениеXML);
Если ТипЗнч(ТоварСсылка) Тип(″СправочникСсылка.Номенклатура″) Тогда

ВызватьИсключение ″Ошибка чтения XML″;
КонецЕсли;
// Создать объект по полученной ссылке.
Товар = ТоварСсылка.ПолучитьОбъект();
// Прочитать признак группы.
ЭтоГруппа = ПрочитатьXML(ЧтениеXML);
Если Товар Неопределено Тогда

Если Товар.ЭтоГруппа ЭтоГруппа Тогда

ВызватьИсключение ″Некорректные данные″;

КонецЕсли;

Иначе

// Создать элемент справочника Номенклатура.

Если ЭтоГруппа = Истина Тогда

Товар = Справочники.Номенклатура.СоздатьГруппу();

Иначе

Товар = Справочники.Номенклатура.СоздатьЭлемент();

КонецЕсли;


// Устанавить значение ссылки для нового объекта.

Товар.УстановитьСсылкуНового(ТоварСсылка);
КонецЕсли;
// Родитель
Товар.Родитель = ПрочитатьXML(ЧтениеXML);
// Наименование
Товар.Наименование = ПрочитатьXML(ЧтениеXML);
// Код
Товар.Код = ПрочитатьXML(ЧтениеXML);
// Прочитать реквизиты, загружаемые только для элемента справочника.
Если Не Товар.ЭтоГруппа Тогда

// ЗакупочнаяЦена

Товар.ЗакупочнаяЦена = ПрочитатьXML(ЧтениеXML);

// ФайлКартинки

Товар.ФайлКартинки = ПрочитатьXML(ЧтениеXML);

// Картинка

Товар.Картинка = ПрочитатьXML(ЧтениеXML);
КонецЕсли;

311

312

Технологии интеграции «1С:Предприятия 8.3»

// Проверить, что текущим узлом является КонецЭлемента.
Если ЧтениеXML.ТипУзла ТипУзлаXML.КонецЭлемента Тогда

ВызватьИсключение ″Ошибка чтения XML″;
КонецЕсли;
// Прочитать следующий узел для завершение чтения элемента XML-документа.
ЧтениеXML.Прочитать();
Возврат Товар;
КонецФункции

В функции ПрочитатьXMLНоменклатура() производится последовательное считывание каждого узла элемента справочника Номенклатура,
записанного вручную в сообщение обмена.
Сначала мы проверяем, что находимся в XML-файле на начале элемента,
затем двигаемся на следующий узел методом Прочитать() объекта
ЧтениеXML. Затем считываем ссылку на элемент справочника и пытаемся
создать объект по полученной ссылке.
Если это сделать не удалось, значит, такого объекта еще не существует,
и мы создаем или новую группу, или новый элемент справочника – в зависимости от значения реквизита ЭтоГруппа, считанного из сообщения обмена.
Затем последовательно считываем (в том порядке, в котором записывали)
и устанавливаем значения «общих» реквизитов, которые мы ранее вручную
выгрузили в процедуре ЗаписатьXMLНоменклатура(), см. листинг 3.67.
В завершение проверяем: если считана вся информация о номенклатуре
(достигнут конец элемента), то двигаемся на следующий элемент данных
в сообщении обмена.
В результате функция возвращает данные о прочитанном элементе номенклатуры в процедуру ПрочитатьСообщениеСИзменениями(), в переменную
Данные (см. листинг 3.68).

В случае стандартного чтения данных методом ПрочитатьXML переменная
Данные будет содержать любой другой объект «1С:Предприятия», полученный из сообщения обмена.
Стратегия разрешения коллизий
Надо понимать, что при вводе новых объектов в различных узлах обмена
ссылки на объекты базы данных будут разными и все новые объекты будут
«доставлены по назначению». Но при обмене уже существующими объектами базы данных (с одинаковыми ссылками), которые был изменены сразу
в нескольких узлах обмена после последнего сеанса обмена, может возник-

Глава 3. Обмен данными

313

нуть коллизия, когда требуется определить, какие изменения принять, а
какие – отклонить.
Чтобы разрешить эту коллизию, в нашем примере мы будем использовать
наиболее простую стратегию «главный – подчиненный». Согласно этой стратегии изменения, полученные от узла обмена, отмеченного в списке узлов
как главный, будут иметь приоритет над остальными.
То есть если для одного и того же объекта обмена данными изменения будут
зарегистрированы и в главном, и в любом другом узле обмена, то изменения
главного узла будут приняты, а остальные – отклонены.
В процедуру ПрочитатьСообщенияСИзменениями() в модуле плана
обмена (см. листинг 3.61) внесем следующие изменения (листинг 3.72).
Листинг 3.72. Процедура «ПрочитатьСообщениеСИзменениями»
Процедура ПрочитатьСообщениеСИзменениями() Экспорт

// Прочитать данные из сообщения в цикле. *** XML-сериализация.
Пока ОбменСУдаленнымиСкладами.ВозможностьЧтенияДанных(ЧтениеXML) Цикл


// Прочитать очередное значение.

Данные = ОбменСУдаленнымиСкладами.ПрочитатьДанные(ЧтениеXML);

// Проверить, нужно ли принимать сообщение от узла-отправителя.
// Если нет, отклонить изменения.
Если Не ОбменСУдаленнымиСкладами.ПринятьИзменения(
ЧтениеСообщения.Отправитель, Данные) Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = Строка(Данные.Метаданные())
+ ″: ″ + Строка(Данные) + ″ – Изменения отклонены″;
Сообщение.Сообщить();

Продолжить;
КонецЕсли;


// Записать полученные данные.

Данные.ОбменДанными.Отправитель = ЧтениеСообщения.Отправитель;

Данные.ОбменДанными.Загрузка = Истина;

Данные.Записать();

КонецЦикла;

КонецЦикла;

КонецПроцедуры

Добавленный фрагмент выделен жирным шрифтом. В этом фрагменте
в цикле мы выполняем чтение данных, содержащихся в сообщении обмена.

314

Технологии интеграции «1С:Предприятия 8.3»

Перед тем как записать данные в базу узла, в функции ПринятьИзменения(), расположенной в общем модуле ОбменСУдаленнымиСкладами
(см. листинг 3.73), проверяется возможность принятия изменений от узлаотправителя сообщения. В параметре Данные в функцию передается
полученный объект обмена, а в параметре Отправитель – ссылка на узелотправитель сообщения.
Листинг 3.73. Функция для разрешения коллизии при обмене данными
Функция ПринятьИзменения(Отправитель, Данные)
Прием = Истина;
// Проверить зарегистрировано ли изменение данных для отправителя.
Если ПланыОбмена.ИзменениеЗарегистрировано(Отправитель, Данные) Тогда


// Разрешить получение данных только от главного узла.

Если Не Отправитель.Главный Тогда

Прием = Ложь;

КонецЕсли;

КонецЕсли;
Возврат Прием;
КонецФункции

С помощью метода менеджера планов обмена ИзменениеЗарегистрировано() мы проверяем, зарегистрировано ли изменение объекта для
узла-отправителя в нашей базе данных. Если да и если отправитель не является главным узлом, мы отклоняем полученные изменения (возвращается
Ложь). Во всех остальных случаях изменения принимаются (возвращается
Истина).
На самом деле алгоритм принятия изменений, реализованный в функции
ПринятьИзменения(), может быть другим, гораздо более сложным
и нестандартным. Но мы показали только самый простой и очевидный
пример разрешения коллизии.
Тестирование работы универсального обмена данными
Запустим «1С:Предприятие», введем некоторые тестовые данные и протестируем работу универсальных механизмов обмена данными.
В списке узлов плана обмена Удаленные склады уже существует одна предопределенная запись, соответствующая нашей информационной базе. Это
и будет центральный узел плана обмена. Установим его код (ЦентОфис)
и название (Центральный офис). Затем добавим в список еще два узла обмена:
Оптовый склад (код Опт) и Розничный склад (код Розница).

Глава 3. Обмен данными

315

При создании каждого из этих узлов нужно указать соответствующий ему
склад. Значение реквизита Склад будет определять стратегию распространения данных по узлам. Для узла Центральный офис склад будет содержать
пустую ссылку, так как в центральный узел должны попадать все данные,
независимо от их привязки к складу.
Отметка Главный будет отвечать за результат разрешения коллизий. Сейчас
ни для какого узла обмена ставить эту отметку не будем, потому что главным
для оптового и розничного узлов является Центральный офис, но он является
предопределенным для той информационной базы, в которой мы находимся.
Следовательно, его отметка в центральной базе невозможна.
Но вот что нужно сделать обязательно – так это после записи оптового
и розничного узлов обмена нажать кнопку Зарегистрировать изменения
и тем самым выполнить начальную синхронизацию этих узлов данными
центральной базы (рис. 3.25).

Рис. 3.25. Создание списка узлов плана обмена «Удаленные склады»
в центральной базе

Затем откроем форму настроек информационной базы и установим значение
константы Префикс номеров как «ЦО», а также зададим путь к каталогу
обмена (e:\Exchange), который будет храниться в константе Каталог обмена
(рис. 3.26).

316

Технологии интеграции «1С:Предприятия 8.3»

Рис. 3.26. Установка настроек информационной базы

После этого внесем в базу центрального узла новую номенклатуру, контрагента и приходные/расходные накладные так, чтобы было хотя бы по одному
документу, относящемуся к каждому из складов.
Теперь нам нужно передать эти изменения узлу Оптовый склад. Для этого
откроем обработку Обмен с удаленными складами, в списке узлов плана
обмена выберем узел, которому будут передаваться данные (Оптовый склад),
и нажмем кнопку Выгрузить данные (рис. 3.27).

Рис. 3.27. Выгрузка данных из центральной базы в удаленный склад

При этом в каталоге обмена появится файл сообщения обмена с именем
Message_ЦентОфис_Опт.xml. Из этого файла изменения будут в дальнейшем
прочитаны в узел с кодом Опт.
Но сначала мы должны создать пустую базу этого узла из выгруженной ранее
конфигурации. Создадим в этой базе такой же список узлов плана обмена
Удаленные склады с такими же кодами узлов, что и в центральной базе. С той
лишь разницей, что у узла Центральный офис мы установим флажок Главный,
а предопределенным узлом здесь будет являться Оптовый склад (рис. 3.28).

Глава 3. Обмен данными

317

Рис. 3.28. Создание списка узлов плана обмена «Удаленные склады»
в базе удаленного склада

Затем откроем форму настроек информационной базы и установим значение
константы Префикс номеров как «ОС», а также зададим путь к каталогу
обмена (e:\Exchange), который будет храниться в константе Каталог обмена.
Теперь откроем обработку Обмен с удаленными складами, в списке узлов плана
обмена выберем узел, из которого данные были выгружены (Центральный
офис), и нажмем кнопку Загрузить данные (рис. 3.29).

Рис. 3.29. Загрузка данных из центральной базы в удаленный склад

318

Технологии интеграции «1С:Предприятия 8.3»

Мы видим, что все данные из центральной базы перенеслись в полном
объеме, за исключением приходных и расходных накладных и их движений.
Согласно стратегии распространения данных в узел Оптовый склад попали
только документы с соответствующим складом.
Данные номенклатуры также перенеслись правильно – из центральной базы
не перенесся артикул номенклатуры. Соответствующий реквизит номенклатуры мы не передавали, так как обменивались данными справочника
в ручном режиме.
Теперь нам осталось проверить, как работает разрешение коллизий.
Добавим новую номенклатуру и изменим цену существующей номенклатуры, а также изменим дату существующей приходной накладной. После
этого откроем обработку Обмен с удаленными складами, в списке узлов плана
обмена выберем узел, которому будут передаваться данные (Центральный
офис), и нажмем кнопку Выгрузить данные.
В центральной базе изменим те же данные, которые мы изменяли в базе оптового узла, затем откроем обработку Обмен с удаленными складами, в списке
узлов плана обмена выберем узел, из которого данные были выгружены
(Оптовый склад), и нажмем кнопку Загрузить данные (рис. 3.30).

Рис. 3.30. Загрузка данных из удаленного склада в центральную базу

Глава 3. Обмен данными

319

При чтении данных мы получим сообщения о том, что изменения данных,
сделанные в оптовом узле, отклонены в пользу изменений центральной базы.
Так произошло потому, что центральный офис является главным узлом по
отношению к оптовому складу. А вот при передаче данных в другую сторону
изменения центральной базы будут приняты и затрут изменения оптового
узла.
Теперь проделаем те же действия для узла обмена Розничный склад
и убедимся, что все работает правильно.
Протестируем также, как работает удаление данных, которых в узле быть не
должно. В розничный склад приходят только накладные с соответствующим
складом. Изменим склад документа на оптовый и отправим это изменение
в центральный офис. Там оно будет принято, так как центральная база
получает все данные независимо от склада. Но при следующей отправке
изменений этого документа в розничный склад, во-первых, документ туда
отправлен не будет, согласно стратегии распространения данных по узлам,
а во-вторых, туда будет послано удаление «неправильного» документа.
В результате он исчезнет из «чужого» узла и появится в оптовом складе при
следующем сеансе обмена данными.
В заключение выполним обмен данными между розничным и оптовым
складом. При использовании универсальных механизмов обмена это
возможно. Все работает так же, как и в случае обмена с центральной базой.
За исключением разрешения коллизий. Поскольку ни один из узлов обмена
не является главным для другого, то изменения, сделанные в «своем» узле,
в результате останутся, а изменения «чужого» узла будут отклонены.
Обмен предопределенными данными
До сих пор мы не заостряли внимание на том, что в обмене данными у нас
участвует справочник Склады, содержащий предопределенные элементы
с именами Основной и Розничный. Однако нужно иметь в виду, что при обмене
предопределенными данными в нераспределенной информационной базе
могут возникнуть проблемы.
Дело в том, что при создании справочников и других объектов конфигурации,
которые могут содержать предопределенные элементы, свойство Обновление
предопределенных данных для этих объектов стандартно устанавливается
в значение Авто. Это приводит к тому, что при реструктуризации базы данных
или при первом обращении к таблице, хранящей данных объекта конфигурации, создаются или обновляются элементы данных, связанных по имени
с предопределенными элементами данных в конфигурации.

320

Технологии интеграции «1С:Предприятия 8.3»

В нашем случае при открытии списка складов в базе оптового и розничного складов будут автоматически созданы элементы справочника Склады,
связанные с предопределенными элементами справочника с именами
Основной и Розничный. При этом свойство ИмяПредопределенныхДанных
этих элементов справочника будет установлено в значение Основной
и Розничный. Изменить эту связь можно только с помощью встроенного
языка.
Но затем при загрузке данных из центральной базы в справочник Склады
будут добавлены еще две записи, связанные с этими же предопределенными
элементами. В результате элементы справочника продублируются из-за того,
что данные из центральной базы будут восприняты как новые, так как у них
будут другие ссылки.
В дальнейшем это приведет к ошибке, так как в системе не может быть двух
объектов, связанных с одинаковым предопределенным элементом. Разработчик должен это отслеживать и не допускать такой ситуации. Например,
можно сделать одну из баз ведущей в части обновления предопределенных
данных, а в других – это обновление запретить.
Режим обновления предопределенных данных можно установить для
конкретного объекта конфигурации с помощью метода встроенного
языка УстановитьОбновлениеПредопределенныхДанных(). Либо
можно установить свойство объекта конфигурации Обновление предопределенных данных в конфигураторе. А также это можно сделать для всей
информационной базы в целом с помощью метода УстановитьОбновлениеПредопределенныхДанныхИнформационнойБазы().
Свойство объектов ОбновлениеПредопределенныхДанных
принимать следующие значения:

может

■■ Не обновлять автоматически – в этом случае платформа не выполняет
создание или обновление элемента данных при создании или изменении
предопределенных данных;
■■ Обновлять автоматически – в этом случае платформа автоматически
создаст (или обновит существующие) элементы данных для новых (или
измененных) предопределенных данных;
■■ Авто – платформа автоматически определяет необходимость выполнения
обновления.
Возможность создания и обновления предопределенных данных определяется последовательным анализом режима обновления установленного
в данных объекта конфигурации, или при конфигурировании этого объекта,
или для всей информационной базы до тех пор, пока не будет обнаружено
значение, отличное от значения Авто.

Глава 3. Обмен данными

321

Сказанное касается только нераспределенных баз данных. Если это периферийный узел РИБ, то предопределенные данные не будут обновлены. Если
проверка выполняется для центрального узла РИБ, обновление предопределенных данных будет выполнено.
Самое простое – это запретить обновление предопределенных данных
справочника складов в конфигураторе. Для этого нужно открыть конфигурацию оптового или розничного склада и в окне редактирования свойств
справочника Склады на закладке Прочее установить свойство Обновление
предопределенных данных в значение Не обновлять автоматически (рис. 3.31).

Рис. 3.31. Редактирование свойств справочника «Склады»

Но мы этого делать не будем, а рассмотрим более универсальный вариант
решения этой проблемы – на уровне всей информационной базы в целом.
Добавим

в конфигурацию константу ИспользоватьОбновлениеПредопределенныхДанных и поместим ее в общую форму констант НастройкиИнформационнойБазы. В модуле менеджера значения этой константы
поместим обработчик события ПриЗаписи и заполним его следующим
образом (листинг 3.74).

322

Технологии интеграции «1С:Предприятия 8.3»

Листинг 3.74. Обработчик события «ПриЗаписи»
Процедура ПриЗаписи(Отказ)
Если Значение = Истина Тогда

УстановитьОбновлениеПредопределенныхДанныхИнформационнойБазы(

ОбновлениеПредопределенныхДанных.ОбновлятьАвтоматически);
Иначе

УстановитьОбновлениеПредопределенныхДанныхИнформационнойБазы(

ОбновлениеПредопределенныхДанных.НеОбновлятьАвтоматически);
КонецЕсли;
КонецПроцедуры

Запустим «1С:Предприятие» и установим флажок использования обновления
предопределенных данных в форме общих настроек центральной базы (рис.
3.32).

Рис. 3.32. Установка настроек информационной базы

Создадим снова базу оптового и/или розничного склада из конфигурации,
откроем окно настроек и запишем этот флажок в снятом состоянии.
Затем откроем список складов. Он будет пустым, так как при записи настроек
для всей информационной базы мы установили режим обновления предопределенных данных в значение Не обновлять автоматически.
Теперь выполним чтение начальных данных от центральной базы. После
этого в списке складов появятся две записи, полученные из центральной
базы, связанные с предопределенными элементами справочника с именами
Основной и Розничный. И это правильно.
Как уже говорилось, автоматическое создание предопределенных данных
в дочерних узлах распределенной информационной базы не производится. Но следует иметь в виду, что в случае отмены назначения главного
узла (например, для выполнения каких-либо специальных действий)
такая информационная база не будет считаться периферийной. В связи
с этим при первом запуске такой информационной базы в режиме
1С:Предприятие режим создания или обновления предопределенных
элементов может быть определен как Обновлять автоматически. Чтобы

Глава 3. Обмен данными

323

избежать этого, можно воспользоваться параметром командной строки
/SetPredefinedDataUpdate или использовать метод глобального
контекста УстановитьОбновлениеПредопределенныхДанныхИнформационнойБазы().
Ручная регистрация изменений
Бывают ситуации, когда автоматическая регистрация изменений некоторых
объектов конфигурации не нужна, так как эти изменения требуется регистрировать только при соблюдении каких-то специфических условий. Или же
необходимо зарегистрировать изменения объекта для какого-либо определенного перечня узлов, отличного от формируемого при авторегистрации.
В этих случаях можно воспользоваться двумя способами: определить обработчики события ПередЗаписью и ПередУдалением модуля объекта или
программно отключить свойство Автозаполнение, очистить набор узловполучателей и сформировать свой собственный список получателей.
Чтобы продемонстрировать эти возможности, внесем небольшие дополнения
в пример, рассмотренный нами выше.
Откроем состав плана обмена УдаленныеСклады и запретим авторегистрацию
для документа РасходнаяНакладная и регистра накопления УчетНоменклатуры
(рис. 3.33).

Рис. 3.33. Авторегистрация объектов, входящих в план обмена «УдаленныеСклады»

324

Технологии интеграции «1С:Предприятия 8.3»

Таким образом, при изменении этих объектов список узлов-получателей
свойства ОбменДанными автоматически заполняться не будет. Покажем, как
зарегистрировать изменения вручную при соблюдении следующих условий:
■■ При обмене каждая накладная должна уйти в свой узел (реквизит Склад
узла плана обмена должен совпасть со складом, выбранным в документе).
■■ Пользователям разрешено менять склад в документе. Если документ уже
ушел в неправильный узел, он должен в нем удалиться и отправиться
в правильный.
Формирование списка узлов-получателей
в обработчиках события «ПриЗаписи»
Для реализации первой задачи в модуле объекта РасходнаяНакладная поместим обработчик события ПередЗаписью и заполним его следующим
образом (листинг 3.75).
Листинг 3.75. Обработчик события «ПередЗаписью» документа «РасходнаяНакладная»
Процедура ПередЗаписью(Отказ)
Если ОбменДанными.Получатели.Автозаполнение Тогда

ОбменСУдаленнымиСкладами.ВключитьРегистрацию(ОбменДанными, Склад);

// Проверить, изменился ли склад в существующем документе.

Если Не ЭтоНовый() Тогда


Запрос = Новый Запрос(″

|ВЫБРАТЬ

|
Склад

|ИЗ

|
Документ.РасходнаяНакладная

|ГДЕ

|
Ссылка = &ТекСсылка″);

Запрос.УстановитьПараметр(″ТекСсылка″, Ссылка);

Выборка = Запрос.Выполнить().Выбрать();

Выборка.Следующий();

Если Выборка.Склад Склад Тогда

ОбменСУдаленнымиСкладами.ВключитьРегистрацию(

ОбменДанными, Выборка.Склад);

КонецЕсли;

КонецЕсли;
КонецЕсли;
// Вывести диагностическое сообщение о регистрации изменений.
ОбменСУдаленнымиСкладами.ВывестиРегистрацию(ЭтотОбъект);
КонецПроцедуры

Глава 3. Обмен данными

325

В данном обработчике (в случае включения режима Автозаполнение) производится проверка соответствия выбранного склада в объекте
и записанного в информационной базе. В случае расхождения документ регистрируется как для нового узла (в соответствии с измененным складом), так
и для старого.
Затем с аналогичной целью поместим в модуле набора записей регистра
накопления УчетНоменклатуры обработчик события ПриЗаписи и заполним
его следующим образом (листинг 3.76).
Листинг 3.76. Обработчик события «ПередЗаписью»
набора записей регистра «УчетНоменклатуры»
Процедура ПередЗаписью(Отказ)
Если ОбменДанными.Получатели.Автозаполнение Тогда

СкладНабора = Неопределено;


Для Каждого Запись Из ЭтотОбъект Цикл

Если СкладНабора = Неопределено

ИЛИ

Запись.Склад СкладНабора Тогда

ОбменСУдаленнымиСкладами.ВключитьРегистрацию(ОбменДанными, Запись.Склад);

КонецЕсли;

СкладНабора = Запись.Склад;

КонецЦикла;


// Выбрать все различные склады регистра с отбором по регистратору.

Запрос = Новый Запрос;

Запрос.Текст = ″ВЫБРАТЬ Различные

|
Склад

|ИЗ

|
РегистрНакопления.УчетНоменклатуры

|ГДЕ

|
Регистратор = &ТекСсылка″;

Запрос.УстановитьПараметр(″ТекСсылка″, Отбор.Регистратор.Значение);

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл


// Включить регистрацию изменений набора записей для каждого склада.

ОбменСУдаленнымиСкладами.ВключитьРегистрацию(ОбменДанными, Выборка.Склад);


КонецЦикла;
КонецЕсли;
// Вывести диагностическую информацию по набору записей.
ОбменСУдаленнымиСкладами.ВывестиРегистрацию(ЭтотОбъект);

КонецПроцедуры

326

Технологии интеграции «1С:Предприятия 8.3»

Обе эти процедуры используют процедуры ВключитьРегистрацию()
и ВывестиРегистрацию(). Эти процедуры мы поместим в общем модуле
ОбменСУдаленнымиСкладами и заполним следующим образом (листинг 3.77,
3.78).
Листинг 3.77. Процедура «ВывестиРегистрацию()»
Процедура ВывестиРегистрацию(Данные) Экспорт
ОбменДанными = Данные.ОбменДанными;
Сообщение = Новый СообщениеПользователю();
Сообщение.Текст = ″ - Регистрация ″ + Данные.Метаданные() + ″: ″ + Строка(Данные);
Сообщение.Сообщить();
// Вывести список узлов, куда будет выполняться регистрация объекта.
Для Каждого Получатель Из ОбменДанными.Получатели Цикл

Если Не ОбменДанными.Отправитель = Получатель Тогда

Сообщение.Текст = ″ - ″ + Получатель;

Сообщение.Сообщить();

КонецЕсли;
КонецЦикла;
КонецПроцедуры

Процедура ВывестиРегистрацию() выводит в окно сообщений данные об
объекте, регистрация которого произошла, и узлах-получателях, для которых
эта регистрация произошла.
Листинг 3.78. Процедура «ВключитьРегистрацию()»
Процедура ВключитьРегистрацию(ОбменДанными, Склад) Экспорт
// Получить список узлов для конкретного склада.
Запрос = Новый Запрос;
Запрос.Текст = ″ВЫБРАТЬ

| УдаленныеСклады.Ссылка

|ИЗ

| ПланОбмена.УдаленныеСклады КАК УдаленныеСклады
|

|ГДЕ

| (УдаленныеСклады.Склад = &Склад ИЛИ

| УдаленныеСклады.Склад = &ПустойСклад)

| И УдаленныеСклады.ЭтотУзел = ЛОЖЬ″;
Запрос.УстановитьПараметр(″Склад″, Склад);
Запрос.УстановитьПараметр(″ПустойСклад″, Справочники.Склады.ПустаяСсылка());
Выборка = Запрос.Выполнить().Выбрать();
// Зарегистировать изменения объекта для выбранных узлов.
Пока Выборка.Следующий() Цикл

ОбменДанными.Получатели.Добавить(Выборка.Ссылка);
КонецЦикла;
КонецПроцедуры

Глава 3. Обмен данными

327

Задача процедуры ВключитьРегистрацию() − определить всех
возможных получателей регистрируемого изменения. В этот список попадут
те узлы, у которых в реквизите Склад содержится ссылка на интересующий
нас склад либо пустая ссылка (что расширяет функциональность решения).
И, естественно, этот получатель не должен быть предопределенным узлом
данного плана обмена.
Регистрация изменений для узла
в обработке для выполнения обмена
До сих пор мы выполняли первоначальную регистрацию изменений для узла
с помощью метода менеджера планов обмена ЗарегистрироватьИзменения() в форме узла обмена, сразу же после его создания.
Теперь нам нужно изменить процедуру регистрации изменений для узла
с учетом ручной регистрации объектов. Будем выполнять полную регистрацию изменений для выбранного узла в обработке для выполнения
обмена ОбменСУдаленнымиСкладами. А также нам понадобится возможность
удаления всех записей регистрации изменений для узла.
Для этого добавим в форму обработки команды УдалитьРегистрациюИзменений и ПолнаяРегистрацияИзменений и перетащим эти команды в окно
элементов формы. У кнопок, соответствующих командам, снимем флажок
у свойства Доступность. Их доступность будет включаться при выборе непредопределенного узла обмена в форме обработки.
Для этого обработчик события ПриИзменении для поля формы УзелОбмена
дополним следующим образом (листинг 3.79).
Листинг 3.79. Обработчик события «ПриИзменении» поля «УзелОбмена»
&НаКлиенте
Процедура УзелОбменаПриИзменении(Элемент)
ДоступностьКнопок = ПолучитьДоступностьПоУзлу(УзелОбмена);
// Установить доступность кнопок в зависимости от установленного узла обмена.
Элементы.ЗагрузитьДанные.Доступность = ДоступностьКнопок;
Элементы.ВыгрузитьДанные.Доступность = ДоступностьКнопок;
Элементы.УдалитьРегистрациюИзменений.Доступность = ДоступностьКнопок;
Элементы.ПолнаяРегистрацияИзменений.Доступность = ДоступностьКнопок;
КонецПроцедуры

Теперь создадим обработчик команды УдалитьРегистрациюИзменений
и заполним следующим образом (листинг 3.80).

328

Технологии интеграции «1С:Предприятия 8.3»

Листинг 3.80. Обработчик команды «УдалитьРегистрациюИзменений»
&НаКлиенте
Процедура УдалитьРегистрациюИзменений(Команда)
ОбменСУдаленнымиСкладами.ПолноеУдалениеРегистрацииДанныхПоУзлу(УзелОбмена);
КонецПроцедуры

В этом обработчике мы вызываем процедуру ПолноеУдалениеРегистрацииДанныхПоУзлу(), в которую передаем ссылку на узел обмена,
для которого должна произойти очистка таблиц регистрации изменений.
Эту процедуру мы поместим в общем модуле ОбменСУдаленнымиСкладами
и заполним следующим образом (листинг 3.81).
Листинг 3.81. Удаление регистрации изменений для узла
Процедура ПолноеУдалениеРегистрацииДанныхПоУзлу(Узел) Экспорт
Сообщение = Новый СообщениеПользователю();
Сообщение.Текст = ″Удаление регистрации всех данных для узла ″ + Узел;
Сообщение.Сообщить();
ПланыОбмена.УдалитьРегистрациюИзменений(Узел);
КонецПроцедуры

Теперь создадим обработчик команды ПолнаяРегистрацияИзменений
и заполним следующим образом (листинг 3.82).
Листинг 3.82. Обработчик команды «ПолнаяРегистрацияИзменений»
&НаКлиенте
Процедура ПолнаяРегистрацияИзменений(Команда)
ОбменСУдаленнымиСкладами.ПолнаяРегистрацияДанныхПоУзлу(УзелОбмена, Ложь);
КонецПроцедуры

В этом обработчике мы вызываем процедуру ПолнаяРегистрацияДанныхПоУзлу(), в которую передаем ссылку на узел обмена, для которого
необходимо выполнить регистрацию изменений. Эту процедуру мы поместим в общем модуле ОбменСУдаленнымиСкладами и заполним следующим
образом (листинг 3.83).

Глава 3. Обмен данными

329

Листинг 3.83. Полная регистрация изменений для узла
Процедура ПолнаяРегистрацияДанныхПоУзлу(Узел, РегистрироватьВсе) Экспорт
// Зарегистрировать изменения всех данных для узла, в случае если
// значения параметра РегистрироватьВсе = Истина или
// значение склада в узле не установлено.
Сообщение = Новый СообщениеПользователю();
Если РегистрироватьВсе Или Узел.Склад.Пустая() Тогда

Сообщение.Текст = ″Регистрация всех данных по узлу ″ + Узел;

Сообщение.Сообщить();

ПланыОбмена.ЗарегистрироватьИзменения(Узел);
Иначе

Сообщение.Текст = ″Регистрация данных по узлу ″ + Узел + ″ -----------″;

Сообщение.Сообщить();

ПланОбмена = Узел.Метаданные();

Состав = ПланОбмена.Состав;


Для Каждого ЭлементСостава Из Состав Цикл

Сообщение.Текст = ″ Регистрация данных ″ + ЭлементСостава.Метаданные

+ ″ по узлу ″ + Узел;

Сообщение.Сообщить();


Если ЭлементСостава.Метаданные = Метаданные.Документы.РасходнаяНакладная Тогда


// Зарегистрировать изменения документов РасходнаяНакладная для узла,

// в случае если

// склад узла совпадает со складом накладной.

// Выбрать документы РасходнаяНакладная с отбором по складу узла.

Запрос = Новый Запрос(″ВЫБРАТЬ

|
Ссылка

|ИЗ

|
Документ.РасходнаяНакладная

|ГДЕ

|
Склад = &Склад″);


Запрос.УстановитьПараметр(″Склад″, Узел.Склад);

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл

// Зарегистрировать изменение документа для узла.

ПланыОбмена.ЗарегистрироватьИзменения(Узел, Выборка.Ссылка);

КонецЦикла;

ИначеЕсли ЭлементСостава.Метаданные

= Метаданные.РегистрыНакопления.УчетНоменклатуры Тогда


// Выбрать все различные регистраторы регистра.

Запрос = Новый Запрос(″ВЫБРАТЬ РАЗЛИЧНЫЕ

|
Регистратор

|ИЗ

|
РегистрНакопления.УчетНоменклатуры

|ГДЕ

|
Склад = &Склад″);


330

Технологии интеграции «1С:Предприятия 8.3»

Запрос.УстановитьПараметр(″Склад″, Узел.Склад);

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл

// Создать набор записей регистра с отбором по регистратору.

НаборЗаписей = РегистрыНакопления

.УчетНоменклатуры.СоздатьНаборЗаписей();

НаборЗаписей.Отбор.Регистратор.Установить(Выборка.Регистратор);

// Зарегистрировать изменения набора записей для узла.

ПланыОбмена.ЗарегистрироватьИзменения(Узел, НаборЗаписей);

КонецЦикла;

Иначе


// Зарегистрировать изменения всех данных объекта для узла.

ПланыОбмена.ЗарегистрироватьИзменения(Узел, ЭлементСостава.Метаданные);

КонецЕсли;


КонецЦикла;

КонецЕсли;
КонецПроцедуры

Во втором параметре РегистрироватьВсе в процедуру ПолнаяРегистрацияДанныхПоУзлу() передается значение типа Булево. Если
это значение Истина, то для данного узла будет выполняться регистрация
изменения всех объектов (невзирая на принадлежность данного узла
к какому-либо определенному складу). Такой же вариант регистрации будет
осуществлен, если в качестве первого параметра будет передан узел, у которого в реквизите Склад содержится пустая ссылка.
Полная регистрация всех объектов производится с помощью метода менеджера планов обмена ЗарегистрироватьИзменения() без указания
второго параметра (его значение по умолчанию Неопределено). При этом
на отключенную авторегистрацию (для документов РасходнаяНакладная
и наборов записей регистра накопления УчетНоменклатуры) внимания
не обращается.
Если в качестве второго параметра передается значение Ложь, расходные
накладные и записи регистра накопления регистрируются как измененные
только в случае совпадения склада со складом, определенным в узле плана
обмена. Все остальные данные регистрируются обычным образом.
Проверим, как это работает. В центральной базе в обработке Обмен с удаленными складами выберем узел обмена Розничный склад. Сначала удалим всю
регистрацию изменений для этого узла обмена с помощью кнопки Удалить
регистрацию изменений, а потом снова выполним полную регистрацию изменений по этому узлу с помощью кнопки Полная регистрация изменений. Затем
нажмем кнопку Выгрузить данные и запишем все изменения, зарегистрированные в центральной базе для розничного склада в файл выгрузки.

Глава 3. Обмен данными

331

После этого создадим пустую базу розничного склада из выгруженной ранее
конфигурации. В этой базе создадим такой же список узлов плана обмена
Удаленные склады с такими же кодами узлов, что и в центральной базе. Установим префикс номеров и каталог обмена в форме настроек информационной
базы так же, как мы это делали раньше. В обработке Обмен с удаленными
складами выберем узел обмена Центральный офис, нажмем кнопку Загрузить
данные и получим все изменения, переданные от центральной базы.
Убедимся, что ручная регистрация изменений работает правильно. При этом
изменения расходных накладных и движений регистра накопления просто
не регистрируются для узлов обмена с несоответствующим складом. То есть
они отфильтровываются не на уровне записи в сообщение обмена, а сразу –
на уровне формирования таблиц регистрации изменений.
Регистрация в произвольные узлы
В нашем примере обмена данными для документов ПриходнаяНакладная была
включена автоматическая регистрация изменений (см. рис. 3.33). Но иногда
требуется зарегистрировать изменения для какого-либо определенного
перечня узлов, отличного от формируемого при авторегистрации.
В этом случае можно программно отключить свойство Автозаполнение,
очистить набор узлов-получателей и сформировать свой собственный список
получателей.
Для этого добавим в форму обработки еще одну команду ЗарегистрироватьПриходные и перетащим ее в окно элементов формы. У соответствующей
кнопки будем устанавливать доступность при выборе непредопределенного узла обмена в форме обработки. Это делается в обработчике события
ПриИзменении поля формы УзелОбмена, как это было показано в предыдущем разделе (см. листинг 3.79).
Теперь создадим обработчик команды ЗарегистрироватьПриходные и заполним
следующим образом (листинг 3.84).
Листинг 3.84. Обработчик команды «ЗарегистрироватьПриходные»
&НаКлиенте
Процедура ЗарегистрироватьПриходные(Команда)
УзлыПолучатели = Новый Массив;
УзлыПолучатели.Добавить(УзелОбмена);
ЗарегистрироватьПриходныеНаСервере(УзлыПолучатели);
КонецПроцедуры

332

Технологии интеграции «1С:Предприятия 8.3»

В этом обработчике мы вызываем процедуру ЗарегистрироватьПриходныеНаСервере(), в которую передаем массив узлов, для которых требуется
зарегистрировать приходные накладные. Для упрощения примера мы будем
добавлять в этот массив только одно значение – ссылку на узел обмена,
выбранный в форме обработки (листинг 3.85).
Листинг 3.85. Процедура «ЗарегистрироватьПриходныеНаСервере»
&НаСервереБезКонтекста
Процедура ЗарегистрироватьПриходныеНаСервере(Узлы)

Выборка = Документы.ПриходнаяНакладная.Выбрать();
Пока Выборка.Следующий() Цикл

Объект = Выборка.ПолучитьОбъект();




// Добавить набор узлов в коллекцию получателей
Объект.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
ЗаполнитьНаборУзлов(Объект.ОбменДанными.Получатели, Узлы);


Объект.Записать();

КонецЦикла;
КонецПроцедуры

В теле процедуры производится выборка всех документов ПриходнаяНакладная. У каждого полученного объекта отключается автозаполнение,
и набор получателей формируется самостоятельно с помощью процедуры
ЗаполнитьНаборУзлов(), в которую передается коллекция узлов-получателей, содержащаяся в свойстве объекта ОбменДанными, и массив узлов,
для которых требуется зарегистрировать приходные накладные (листинг
3.86).
Листинг 3.86. Процедура «ЗаполнитьНаборУзлов()»
Процедура ЗаполнитьНаборУзлов(Набор, Список)
Набор.Очистить();
Для каждого Элемент Из Список Цикл

Набор.Добавить(Элемент);
КонецЦикла;
КонецПроцедуры

В этой процедуре коллекция узлов-получателей, для которых регистрируются изменения объекта, очищается. И в цикле обхода элементов массива,
полученного в параметре Список, эта коллекция заполняется заново новыми
значениями.

Глава 3. Обмен данными

333

При такой регистрации будут зарегистрированы изменения всех приходных
накладных, невзирая на соответствие склада документа и склада узла обмена.
Но при выгрузке данных в узел «неправильные» накладные будут отсеяны
согласно стратегии распространения данных, используемой в этом примере
обмена данными.

Использование транзакций
при организации обмена
При реализации обмена, как посредством универсального механизма обмена,
так и с использованием механизма распределенных информационных баз,
одна из задач, которые требуют решения, – это обеспечение целостности
и согласованности данных.
Данная задача при использовании универсального обмена может решаться
с использованием транзакций (листинг 3.87).
Листинг 3.87. Пример использования транзакций
НачатьТранзакцию();
// обработка данных

ЗафиксироватьТранзакцию();

При выгрузке данных в механизме распределенных информационных баз
используется метод ЗаписатьИзменения() менеджера планов обмена
(листинг 3.88).
Листинг 3.88. Пример использования метода «ЗаписатьИзменения()»
ПланыОбмена.ЗаписатьИзменения(ЗаписьСообщения, 0);

Второй параметр метода указывает количество элементов, записываемых
в одной транзакции. Значение 0 указывает на то, что запись всех измененных
данных производится в одной транзакции.
Такой подход (когда вся выгрузка или загрузкаосуществляется в одной транзакции) обладает как определенными преимуществами, так и определенными
недостатками.
Преимущества такого подхода заключаются в том, что, например, в файловом
варианте работы действия, сгруппированные в одну транзакцию, выполняются значительно быстрее (до определенного предела). Кроме того, при

334

Технологии интеграции «1С:Предприятия 8.3»

выгрузке данных использование одной транзакции позволяет избежать
несогласованности выгружаемых данных (например, когда после выгрузки
документа, но до выгрузки наборов записей регистров произошло перепроведение документа).
Минусом такого подхода является снижение параллельности работы пользователей, так как выгружаемые данные будут заблокированы до окончания
транзакции.
Кроме того, необходимо учитывать объем изменений, выполняемых в одной
транзакции. Например, в файловом варианте все изменения, произведенные
транзакцией, накапливаются в оперативной памяти, что при записи больших
объемов данных может привести к исчерпыванию свободной памяти.
В клиент-серверном варианте такой опасности нет, но все-таки записывать большие объемы данных в одной транзакции не рекомендуется (из-за
проблем параллельности блокировки тоже требуют ресурса сервера баз
данных и т. п.).
При обмене большими порциями данных имеет смысл использовать
несколько транзакций при загрузке или выгрузке данных.
Например, в алгоритме загрузки данных можно использовать следующий
фрагмент кода, позволяющий разбивать процесс загрузки на несколько
транзакций по 1000 элементов данных (в данном случае величина 1000 −
условная) – листинг 3.89.
Листинг 3.89. Пример использования нескольких транзакций при выгрузке данных
Счетчик = 0;
НачатьТранзакцию();
// обработка данных

Если Счетчик = 1000 Тогда
Счетчик = 0;
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
КонецЕсли;
// обработка данных

ЗафиксироватьТранзакцию();

В случаях использования механизма распределенных информационных баз
количество объектов указывается вторым параметром метода ЗаписатьИзменения() (листинг 3.90).

Глава 3. Обмен данными

335

Листинг 3.90. Пример указания количества элементов,
записываемых в одной транзакции
ПланыОбмена.ЗаписатьИзменения(ЗаписьСообщения, 1000);

Следует отметить, что зачастую в каждом конкретном случае необходимо
искать компромисс между скоростью и параллельностью работы пользователей.

Методика включения в сообщение обмена
дополнительной информации
В некоторых случаях обмен данными должен сопровождаться передачей
в сообщении обмена служебной дополнительной информации. Передачу
подобной информации можно осуществлять непосредственно в теле сообщения обмена. Дополнительную информацию можно размещать в теле
сообщения как при реализации универсального обмена, так и в случае
работы с распределенными информационными базами.
При универсальном обмене это делается довольно просто. При выгрузке
данных (в любой момент: в начале сообщения, в середине, в конце) можно
записать в тело сообщения специализированный элемент XML (с нужным
наполнением) (листинг 3.91).
Листинг 3.91. Пример записи служебной информации
ЗаписьXML.ЗаписатьНачалоЭлемента(″info″);
ЗаписьXML.ЗаписатьАтрибут(″содержание″, ″Дополнительная информация″);
ЗаписьXML.ЗаписатьТекст(″передаваемая информация″);
ЗаписьXML.ЗаписатьКонецЭлемента();

При чтении необходимо предусмотреть возможность чтения данного специализированного узла (листинг 3.92).
Листинг 3.92. Пример чтения служебной информации
Если ЧтениеXML.Имя=″info″ Тогда
// Прочитать текст xml элемента.
ЧтениеXML.Прочитать();
// Прочитать конец элемента info.
ЧтениеXML.Прочитать();
// Спозиционироваться на начале элемента v8de:Changes
ЧтениеXML.Прочитать();
КонецЕсли;

336

Технологии интеграции «1С:Предприятия 8.3»

При необходимости указания дополнительных данных при работе с распределенной информационной базой можно использовать следующий подход
(листинг 3.93).
Листинг 3.93. Пример передачи дополнительной информации
в сообщении распределенной информационной базы
// Создать объект записи XML.
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(″c:\out.xml″);
ЗаписьXML.ЗаписатьОбъявлениеXML();
// Создать новое сообщение.
Узел = ПланыОбмена.Магазины.НайтиПоКоду(″Магазин″);
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, Узел);
// Записать дополнительную информацию.
ЗаписьXML.ЗаписатьНачалоЭлемента(″info″);
ЗаписьXML.ЗаписатьАтрибут(″содержание″, ″Код″);
ВключитьСтроку = ″
|Событие = Документы.Событие.СоздатьДокумент();
|Событие.Дата = ТекущаяДата();
|Событие.ОписаниеСобытия = ″″Сформировать внутренние заказы″″;
|// заполнение других реквизитов документа
|Событие.Записать(); ″;
ЗаписьXML.ЗаписатьТекст(ВключитьСтроку);
ЗаписьXML.ЗаписатьКонецЭлемента();
// Записать изменения.
ПланыОбмена.ЗаписатьИзменения(ЗаписьСообщения, 0);
ЗаписьСообщения.ЗакончитьЗапись();
ЗаписьXML.Закрыть();

В результате выполнения получаем следующее сообщение (приведен
начальный фрагмент) – листинг 3.94.
Листинг 3.94. Фрагмент сообщения обмена



Магазины

Магазин

ЦентрОфис

1

0




































COPY архив.zip.001 /B + архив.zip.002 /B архив.zip

Примеры работы
Ниже мы рассмотрим примеры записи и чтения файлов в/из архивов, использующие современные методы передачи файлов на сервер для их дальнейшей
обработки и затем получения их с сервера. А также покажем возможность
работы с архивами с помощью двоичных данных.
Поскольку обмен файлами между клиентом и сервером через временное
хранилище, а также работа с двоичными данными отдельно подробно
рассматривались в соответствующих разделах книги, в этих примерах мы
будем пояснять только новые, не описанные ранее возможности работы.

490

Технологии интеграции «1С:Предприятия 8.3»

Добавление в архив файлов по маске
Рассмотрим пример добавления в архив файлов из выбранного пользователем каталога и соответствующих маске (в данном случае – «*.jpg»). Данная
задача может быть выполнена с помощью следующего программного кода
(листинги 6.137, 6.138, 6.139, 6.140).
Листинг 6.137. Пример добавления файлов в ZIP-архив
&НаКлиенте
Процедура ДобавлениеФайловВАрхив(Команда)
ВыбратьКаталог(″Выбор каталога для поиска файлов″,

Новый ОписаниеОповещения(″ДобавитьФайлыВАрхивЗавершение″, ЭтотОбъект));
КонецПроцедуры
Листинг 6.138. Процедура «ВыбратьКаталог()»
&НаКлиенте
Процедура ВыбратьКаталог(ЗаголовокДиалога, Оповещение)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.ВыборКаталога);
Диалог.Заголовок = ЗаголовокДиалога;
Диалог.Показать(Оповещение);
КонецПроцедуры
Листинг 6.139. Обработчик оповещения «ДобавитьФайлыВАрхивЗавершение()»
&НаКлиенте
Процедура ДобавитьФайлыВАрхивЗавершение(ВыбранныеФайлы, Дополнительно) Экспорт
Если ВыбранныеФайлы = Неопределено Тогда

Возврат;
КонецЕсли;
МаскаПоиска = ВыбранныеФайлы[0] + ″\*.jpg″;
АдресАрхиваВХранилище = ДобавитьФайлыВАрхивНаСервере(МаскаПоиска);
НачатьПолучениеФайлаССервера(, АдресАрхиваВХранилище, ″c:\temp\pictures.zip″);
КонецПроцедуры
Листинг 6.140. Функция «ДобавитьФайлыВАрхивНаСервере()»
&НаСервереБезКонтекста
Функция ДобавитьФайлыВАрхивНаСервере(МаскаПоиска)
Архив = Новый ЗаписьZIPФайла();

Глава 6. Файловое взаимодействие

491


Архив.Добавить(МаскаПоиска,

РежимСохраненияПутейZIP.СохранятьОтносительныеПути, РежимОбработкиПодкаталоговZIP.

ОбрабатыватьРекурсивно);

ДанныеАрхива = Архив.ПолучитьДвоичныеДанные(″″, ″″,

МетодСжатияZIP.Сжатие, УровеньСжатияZIP.Максимальный, МетодШифрованияZIP.Zip20);

Возврат ПоместитьВоВременноеХранилище(ДанныеАрхива);
КонецФункции

Сначала пользователю показывается диалог выбора каталога для
поиска файлов, помещаемых в архив. Когда каталог выбран, на его
основе формируется маска поиска и передается в функцию ДобавитьФайлыВАрхивНаСервере(), в которой новый архив создается как
неинициализированный объект ЗаписьZIPФайла. Затем файлы по маске
помещаются в архив и вызывается метод этого объекта ПолучитьДвоичныеДанные(), который получает ZIP-архив в виде двоичных данных
и закрывает архив аналогично методу Записать(). Эти двоичные данные
возвращаются на клиент в процедуру ДобавитьФайлыВАрхивЗавершение(), где файл архива получается в виде локального файла с явно
заданным именем.
Добавление выбранных файлов в архив
Рассмотрим пример добавления в архив произвольных файлов, выбранных
пользователем. Данная задача может быть выполнена с помощью следующего программного кода (листинги 6.141, 6.142, 6.143, 6.144).
Листинг 6.141. Пример добавления файлов в ZIP-архив
&НаКлиенте
Процедура ЗаписатьФайлыВАрхив(Команда)
ПоместитьФайлыВАрхив(Новый ОписаниеОповещения(

″ПоместитьФайлыВАрхивЗавершение″, ЭтотОбъект));
КонецПроцедуры
Листинг 6.142. Процедура «ПоместитьФайлыВАрхив()»
&НаКлиенте
Процедура ПоместитьФайлыВАрхив(ОповещениеОЗавершении)
ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов(

″Выберите файлы для помещения в архив″, Истина);
НачатьПомещениеФайловНаСервер(ОповещениеОЗавершении, , , ПараметрыДиалога,

УникальныйИдентификатор);
КонецПроцедуры

492

Технологии интеграции «1С:Предприятия 8.3»

Листинг 6.143. Обработчик оповещения «ПоместитьФайлыВАрхивЗавершение()»
&НаКлиенте
Процедура ПоместитьФайлыВАрхивЗавершение(ПомещенныеФайлы, Дополнительно) Экспорт

Если ПомещенныеФайлы = Неопределено Тогда

Возврат;
КонецЕсли;
АдресаДобавляемыхФайлов = Новый Массив;
ИменаДобавляемыхФайлов = Новый Массив;
Для Каждого ДобавляемыйФайл Из ПомещенныеФайлы Цикл

АдресаДобавляемыхФайлов.Добавить(ДобавляемыйФайл.Адрес);

ИменаДобавляемыхФайлов.Добавить(ДобавляемыйФайл.СсылкаНаФайл.Имя);
КонецЦикла;
АдресФайлаВХранилище = ЗаписатьФайлыВАрхивНаСервере(

АдресаДобавляемыхФайлов, ИменаДобавляемыхФайлов);
НачатьПолучениеФайлаССервера(, АдресФайлаВХранилище, ″e:\test\arc.zip″);

КонецПроцедуры
Листинг 6.144. Функция «ЗаписатьФайлыВАрхивНаСервере()»
&НаСервереБезКонтекста
Функция ЗаписатьФайлыВАрхивНаСервере(МассивАдресовФайлов, МассивИменФайлов)
Архив = Новый ЗаписьZIPФайла();
Индекс = 0;
Для Каждого АдресДобавляемогоФайла Из МассивАдресовФайлов Цикл

ДанныеФайла = ПолучитьИзВременногоХранилища(АдресДобавляемогоФайла);

ИмяФайла = КаталогВременныхФайлов() + МассивИменФайлов[Индекс];

ДанныеФайла.Записать(ИмяФайла);

Архив.Добавить(ИмяФайла,

РежимСохраненияПутейZIP.НеСохранятьПути,

РежимОбработкиПодкаталоговZIP.НеОбрабатывать);


Индекс = Индекс + 1;

КонецЦикла;
ДанныеАрхива = Архив.ПолучитьДвоичныеДанные(″″, ″″,

МетодСжатияZIP.Сжатие, УровеньСжатияZIP.Максимальный, МетодШифрованияZIP.Zip20);

Возврат ПоместитьВоВременноеХранилище(ДанныеАрхива);
КонецФункции

Глава 6. Файловое взаимодействие

493

Сначала пользователю показывается диалог множественного выбора файлов,
добавляемых в архив, и начинается помещение выбранных файлов на сервер.
Когда все файлы помещены, адреса во временном хранилище и имена помещенных файлов в виде двух отдельных массивов передаются в функцию
ЗаписатьФайлыВАрхивНаСервере().

В этой функции элементы массива помещенных файлов обходятся в цикле,
данные каждого файла записываются во временный файл и этот файл
добавляется в неинициализированный архив. Затем методом ПолучитьДвоичныеДанные() этот архив получается в виде двоичных данных, которые
функция возвращает на клиент в процедуру ПоместитьФайлыВАрхивЗавершение(), где файл архива получается в виде локального zip-файла.
Распаковка файлов из архива
Рассмотрим пример распаковки файлов из архива с паролем, указанным
пользователем. Данная задача может быть выполнена с помощью следующего программного кода (листинги 6.145, 6.146, 6.147, 6.148, 6.149).
Листинг 6.145. Пример распаковки файлов из ZIP-архива
&НаКлиенте
Процедура РаспаковкаФайловИзАрхива(Команда)
// Показать ввод пароля на архив
Подсказка = ″Введите пароль на архив″;
Оповещение = Новый ОписаниеОповещения(″ПослеВводаСтроки″, ЭтотОбъект);
ПоказатьВводСтроки(Оповещение, ″″, Подсказка, 0, Ложь);
КонецПроцедуры
Листинг 6.146. Процедура «ПослеВводаСтроки()»
&НаКлиенте
Процедура ПослеВводаСтроки(Пароль, Параметры) Экспорт
Если НЕ Пароль = Неопределено Тогда

ПоместитьФайлАрхива(Новый ОписаниеОповещения(

″РаспаковкаФайловИзАрхиваЗавершение″, ЭтотОбъект, Пароль));
КонецЕсли;
КонецПроцедуры
Листинг 6.147. Процедура «ПоместитьФайлАрхива()»
&НаКлиенте
Процедура ПоместитьФайлАрхива(ОповещениеОЗавершении)
НачатьПомещениеФайлаНаСервер(ОповещениеОЗавершении, , , ″″, , УникальныйИдентификатор);
КонецПроцедуры

494

Технологии интеграции «1С:Предприятия 8.3»

Листинг 6.148. Обработчик оповещения «РаспаковкаФайловИзАрхиваЗавершение()»
&НаКлиенте
Процедура РаспаковкаФайловИзАрхиваЗавершение(ОписаниеПомещенногоФайла, Пароль) Экспорт

Если ОписаниеПомещенногоФайла = Неопределено Тогда

Возврат;
КонецЕсли;
ОписаниеИзвлеченныхФайлов = РаспаковкаФайловИзАрхиваНаСервере(

ОписаниеПомещенногоФайла.Адрес, Пароль);
Сообщение = Новый СообщениеПользователю();
ИзвлеченныеФайлы = Новый Массив;
Для Каждого Файл Из ОписаниеИзвлеченныхФайлов Цикл

ИзвлеченныеФайлы.Добавить(Новый ОписаниеПередаваемогоФайла(

Файл.ИмяФайла, Файл.АдресФайла));

Сообщение.Текст = ″Распаковывается файл: ″ + Файл.ИмяФайла;

Сообщение.Сообщить();
КонецЦикла;
ПараметрыДиалога = Новый ПараметрыДиалогаПолученияФайлов(

″Выберите каталог для сохранения файлов″, Истина);
НачатьПолучениеФайловССервера(ИзвлеченныеФайлы, ПараметрыДиалога);
КонецПроцедуры
Листинг 6.149. Функция «РаспаковкаФайловИзАрхиваНаСервере()»
&НаСервереБезКонтекста
Функция РаспаковкаФайловИзАрхиваНаСервере(АдресФайлаАрхива, Пароль)
ДанныеФайла = ПолучитьИзВременногоХранилища(АдресФайлаАрхива);
МассивИзвлеченныхФайлов = Новый Массив();
ВременныйКаталог = КаталогВременныхФайлов();
Архив = Новый ЧтениеZipФайла(ДанныеФайла.ОткрытьПотокДляЧтения(), Пароль);

Для каждого Элемент из Архив.Элементы Цикл


Архив.Извлечь(Элемент, ВременныйКаталог,РежимВосстановленияПутейФайловZIP.

НеВосстанавливать,

?(Элемент.Зашифрован, Пароль, ″″));


ПолныйПутьКИзвлеченномуФайлу = ВременныйКаталог + Элемент.Имя;

АдресИзвлеченногоФайла = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(

ПолныйПутьКИзвлеченномуФайлу));


ДанныеИзвлеченногоФайла = Новый Структура(″АдресФайла, ИмяФайла″,);

ДанныеИзвлеченногоФайла.АдресФайла = АдресИзвлеченногоФайла;

ДанныеИзвлеченногоФайла.ИмяФайла = Элемент.Имя;

Глава 6. Файловое взаимодействие

495



МассивИзвлеченныхФайлов.Добавить(ДанныеИзвлеченногоФайла);


УдалитьФайлы(ПолныйПутьКИзвлеченномуФайлу);

КонецЦикла;
Архив.Закрыть();
Возврат МассивИзвлеченныхФайлов;

КонецФункции

Сначала пользователю показывается диалог с запросом пароля на чтение
архива. Затем пользователю предлагается выбрать файл архива, и начинается
помещение выбранного файла на сервер. Когда файл помещен, вызывается
функция РаспаковкаФайловИзАрхиваНаСервере(), в которую передаются адрес помещенного файла и введенный пользователем пароль.
В этой функции данные файла-архива получаются из временного хранилища
и с помощью метода ОткрытьПотокДляЧтения() поток двоичных данных
открывается для чтения. Затем на основе этого потока создается объект
ЧтениеZipФайла.

После этого элементы архива обходятся в цикле, каждый файл извлекается из архива во временный каталог методом Извлечь(). На каждом
шаге цикла заполняется массив структур, содержащих адреса во
временном хранилище и имена извлеченных из архива файлов. Этот
массив МассивИзвлеченныхФайлов функция возвращает в процедуру
РаспаковкаФайловИзАрхиваЗавершение(), в которой переданные
разархивированные файлы получаются на клиенте согласно их описаниям.
Разделение и объединение файлов архива
Как уже говорилось, для работы с файлами архивов большого объема
можно использовать функции глобального контекста РазделитьФайл()
и ОбъединитьФайлы(). Но во многих случаях вместо них более эффективно использовать методы РазделитьДвоичныеДанные() и СоединитьДвоичныеДанные(), потому что в этом случае нет необходимости
предварительно сохранять двоичные данные в файл. Кроме того, при обмене
с сервером через временное хранилище файлы передаются как раз в виде
двоичных данных.
Рассмотрим пример разделения на части файла архива. Данная задача может
быть выполнена с помощью следующего программного кода (листинги 6.150,
6.151).

496

Технологии интеграции «1С:Предприятия 8.3»

Листинг 6.150. Пример разделения ZIP-архива
&НаКлиенте
Процедура РазделитьФайлыАрхива(Команда)
ПоместитьФайлАрхива(Новый ОписаниеОповещения(

″РазделитьФайлыАрхиваЗавершение″, ЭтотОбъект));
КонецПроцедуры
Листинг 6.151. Обработчик оповещения «РазделитьФайлыАрхиваЗавершение()»
&НаКлиенте
Процедура РазделитьФайлыАрхиваЗавершение(ОписаниеПомещенногоФайла, Дополнительно) Экспорт

Если ОписаниеПомещенногоФайла = Неопределено Тогда

Возврат;
КонецЕсли;
ДанныеФайлаАрхива = ПолучитьИзВременногоХранилища(ОписаниеПомещенногоФайла.Адрес);
МассивДанныхЧастей = РазделитьДвоичныеДанные(ДанныеФайлаАрхива, 1024 * 1024);
МассивЧастичныхФайлов = Новый Массив;
Индекс = 0;
Для Каждого ДанныеЧасти Из МассивДанныхЧастей Цикл


АдресФайла = ПоместитьВоВременноеХранилище(ДанныеЧасти);

ИмяФайла = ОписаниеПомещенногоФайла.СсылкаНаФайл.Имя + ″_″ + СокрЛП(Строка(Индекс));

МассивЧастичныхФайлов.Добавить(

Новый ОписаниеПередаваемогоФайла(ИмяФайла, АдресФайла));

Индекс = Индекс + 1;

КонецЦикла;
ПараметрыДиалога = Новый ПараметрыДиалогаПолученияФайлов(

″Выберите каталог для сохранения файлов″, Истина);
НачатьПолучениеФайловССервера(МассивЧастичныхФайлов, ПараметрыДиалога);
КонецПроцедуры

Сначала пользователю показывается диалог для выбора файла архива, и начинается помещение выбранного файла архива на сервер. Когда файл помещен,
данные архива получаются из временного хранилища и с помощью метода
РазделитьДвоичныеДанные() двоичные данные файла делятся на части
заданного в байтах размера.
После этого в цикле обхода получившегося массива двоичных данных на
основе адреса во временном хранилище и имени каждой части создается
массив описаний передаваемых файлов, согласно которому файлы частей
сохраняются в выбранный пользователем каталог локальной файловой
системы. Имя файла части формируется на основе имени файла архива
с добавлением постфикса с порядковым номером части.

Глава 6. Файловое взаимодействие

497

Процедура ПоместитьФайлАрхива() была приведена в листинге 6.147.

Рассмотрим теперь обратный пример объединения файлов архива. Данная
задача может быть выполнена с помощью следующего программного кода
(листинги 6.152, 6.153).
Листинг 6.152. Пример объединения файлов из ZIP-архива
&НаКлиенте
Процедура ОбъединитьФайлыАрхива(Команда)
ПоместитьФайлыВАрхив(Новый ОписаниеОповещения(

″ОбъединитьФайлыАрхиваЗавершение″, ЭтотОбъект));
КонецПроцедуры
Листинг 6.153. Обработчик оповещения «ОбъединитьФайлыАрхиваЗавершение()»
&НаКлиенте
Процедура ОбъединитьФайлыАрхиваЗавершение(ПомещенныеФайлы, Дополнительно) Экспорт

Если ПомещенныеФайлы = Неопределено Тогда

Возврат;
КонецЕсли;
ДанныеОбъединяемыхФайлов = Новый Массив;
Для Каждого ЧастичныйФайл Из ПомещенныеФайлы Цикл

ДанныеОбъединяемыхФайлов.Добавить(

ПолучитьИзВременногоХранилища(ЧастичныйФайл.Адрес));
КонецЦикла;
ОбъединенныеДанные = СоединитьДвоичныеДанные(ДанныеОбъединяемыхФайлов);
АдресФайлаВХранилище = ПоместитьВоВременноеХранилище(ОбъединенныеДанные);
НачатьПолучениеФайлаССервера(, АдресФайлаВХранилище, ″c:\temp\pict.zip″);/

КонецПроцедуры

Сначала пользователю показывается диалог множественного выбора
частичных файлов архива, которые требуется объединить, и начинается помещение выбранных файлов на сервер. Когда все файлы помещены, данные
частичных файлов получаются из временного хранилища и добавляются
в массив ДанныеОбъединяемыхФайлов.
На основе этого массива с помощью метода СоединитьДвоичныеДанные() двоичные данные объединяются, помещаются во временное
хранилище и затем сохраняются в локальную файловую систему в виде
целого zip-архива.
Процедура ПоместитьФайлыВАрхив() была приведена в листинге 6.142.

498

Технологии интеграции «1С:Предприятия 8.3»

DBF-файлы
Для работы с базами данных формата DBF в системе имеется специальный
объект – XBase. Механизм работы с базами данных формата DBF предназначен для обеспечения возможности манипулирования ими непосредственно
из встроенного языка системы «1С:Предприятие». Возможно практически
любое манипулирование данными.
Помимо работы с существующими базами данных, объект XBase имеет
набор методов, позволяющих создать новую базу данных произвольной
структуры, новые индексы и новый индексный файл. Следует отметить, что
если использование методов, изменяющих структуру базы данных, возможно
только для объектов, не связанных с существующей базой данных (т. е. для
вновь создаваемых баз данных), то создание новых индексов и индексного
файла возможно как для создаваемых баз данных, так и для уже существующих и открытых.
Для записи данных в файл DBF может использоваться следующий фрагмент
кода (листинг 6.154).
Листинг 6.154. Пример записи данных в файл DBF
&НаКлиенте
Процедура ЗаписьДанных(Команда)
Путь = ″c:\temp\″;
БД = Новый XBase;
БД.Поля.Добавить(″CODE″, ″S″, 9);
БД.Поля.Добавить(″NAME″, ″S″, 40);
БД.Поля.Добавить(″DATA_B″, ″S″, 10);
БД.Поля.Добавить(″CHILD″, ″S″, 1);
БД.СоздатьФайл(Путь + ″list.dbf″, Путь + ″index.cdx″);
БД.Индексы.Добавить(″IDXCODE″, ″CODE″);
ИБД = БД.СоздатьИндексныйФайл(Путь + ″index.cdx″);
БД.АвтоСохранение = Истина;
// Получить данные сотрудников в виде одной большой строки.
СтрокаРазделителя = ″*″;
СтрокаСотрудников = ПолучитьСтрокиСотрудников(СтрокаРазделителя);
// Получить массив строк для каждого сотрудника.
СтрокиСотрудников = СтрРазделить(СтрокаСотрудников, Символы.ПС);
Для ТекущаяСтрока = 0 По СтрокиСотрудников.Количество() - 1 Цикл

// Получить данные каждого сотрудника.

Данные = СтрРазделить(СтрокиСотрудников[ТекущаяСтрока], СтрокаРазделителя);

БД.Добавить();


БД.CODE = Данные[0];

БД.NAME = Данные[1];

БД.DATA_B = Данные[2];

Глава 6. Файловое взаимодействие

499


БД.CHILD = Данные[3];
КонецЦикла;
БД.ЗакрытьФайл();
КонецПроцедуры

Создается файл, у которого определяются четыре поля (CODE, NAME, DATA_B,
CHILD). Все поля имеют строковый тип.
У создаваемого DBF-файла определяется один индексный файл. Следует
отметить, что у DBF-файла может быть определено несколько индексов, но
создаваемый при этом индексный файл всегда один.
При записи данных следует обращать внимание на свойство АвтоСохранение
объекта XBase. Если оно установлено в значение Истина, то добавление
записи в файл производится при вызове следующего метода Добавить(),
создающего новую запись.
Из процедуры ЗаписьДанных() вызывается серверная функция ПолучитьСтрокиСотрудников(), в которой в цикле обхода выборки элементов
справочника Сотрудники формируются строки с данными сотрудников
и в виде одной большой строки возвращаются на клиент (см. листинг 6.155).
Затем из этой строки получается массив строк с данными для каждого
сотрудника и каждая строка разбирается на структуры с помощью функции
глобального контекста СтрРазделить(). Полученные структуры с данными
каждого сотрудника записываются в соответствующие поля новой записи
DBF-файла.
Листинг 6.155. Функция «ПолучитьСтрокиСотрудников()»
&НаСервере
Функция ПолучитьСтрокиСотрудников(Разделитель)
СтрокаСотрудника = Новый Массив();
МассивСтрокСотрудников = Новый Массив();
Выборка = Справочники.Сотрудники.Выбрать();
Пока Выборка.Следующий() Цикл

СтрокаСотрудника.Очистить();

СтрокаСотрудника.Добавить(Выборка.Код);

СтрокаСотрудника.Добавить(Выборка.Наименование);

СтрокаСотрудника.Добавить(Формат(Выборка.ДатаРождения, ″ДЛФ=D″));

СтрокаСотрудника.Добавить(Выборка.КоличествоДетей);


Данные = СтрСоединить(СтрокаСотрудника, Разделитель);

МассивСтрокСотрудников.Добавить(Данные);
КонецЦикла;
Возврат СтрСоединить(МассивСтрокСотрудников, Символы.ПС);
КонецФункции

500

Технологии интеграции «1С:Предприятия 8.3»

Для чтения данных может использоваться следующий фрагмент кода
(листинг 6.156).
Листинг 6.156. Пример чтения данных из файла DBF
&НаКлиенте
Процедура ЧтениеДанных(Команда)
Путь = ″c:\temp\″;
БД = Новый XBase;
БД.ОткрытьФайл(Путь + ″list.dbf″, Путь + ″index.cdx″);
Сообщение = Новый СообщениеПользователю();
Пока Истина Цикл
Сообщение.Текст = БД.CODE + ″ ″ + БД.Name;

Сообщение.Сообщить();

Если Не БД.Следующая() Тогда
Прервать;
КонецЕсли;
КонецЦикла;
БД.ЗакрытьФайл();
КонецПроцедуры

В приведенном фрагменте не используется определенный при записи индекс,
порядок отображения данных может быть следующий (фактически это соответствует физическому положению записей в файле) – листинг 6.157.
Листинг 6.157. Порядок отображения данных
000000001 Алексеев Сергей Иванович
REST-0003 Артемов Игорь Владимирович
000000002 Смирнова Светлана Ивановна

Изменим код, добавив в него использование индекса (листинг 6.158).
Листинг 6.158. Пример чтения данных из файла DBF
&НаКлиенте
Процедура ЧтениеДанных(Команда)
Путь = ″c:\temp\″;
БД = Новый XBase;
БД.ОткрытьФайл(Путь + ″list.dbf″, Путь + ″index.cdx″);
БД.ТекущийИндекс = БД.Индексы.IDXCODE;
Сообщение = Новый СообщениеПользователю();
Пока Истина Цикл
Сообщение.Текст = БД.CODE + ″ ″ + БД.Name;

Глава 6. Файловое взаимодействие

501




Сообщение.Сообщить();
Если Не БД.Следующая() Тогда
Прервать;
КонецЕсли;
КонецЦикла;
БД.ЗакрытьФайл();

КонецПроцедуры

В этом случае в результате будут получены следующие записи, отсортированные в порядке возрастания кодов сотрудников (листинг 6.159).
Листинг 6.159. Порядок отображения данных
000000001 Алексеев Сергей Иванович
000000002 Смирнова Светлана Ивановна
REST-0003 Артемов Игорь Владимирович

При этом надо иметь в виду, что при открытии файла объект XBase позиционируется на записи, которая является физически первой в файле. Однако
с точки зрения используемого индекса она может быть далеко не первой.
При выполнении метода Следующая() выбирается следующая запись
в индексном файле. Поэтому, для того чтобы обойти все записи, необходимо
явно использовать метод Первая(), как показано ниже (листинг 6.160).
Листинг 6.160. Пример использования метода «Первая()»
&НаКлиенте
Процедура ЧтениеДанных(Команда)
Путь = ″c:\temp\″;
БД = Новый XBase;
БД.ОткрытьФайл(Путь + ″list.dbf″, Путь + ″index.cdx″);
БД.ТекущийИндекс = БД.Индексы.IDXCODE;
Сообщение = Новый СообщениеПользователю();
БД.Первая();
Пока Истина Цикл
Сообщение.Текст = БД.CODE + ″ ″ + БД.Name;

Сообщение.Сообщить();

Если Не БД.Следующая() Тогда
Прервать;
КонецЕсли;
КонецЦикла;
БД.ЗакрытьФайл();
КонецПроцедуры

502

Технологии интеграции «1С:Предприятия 8.3»

Индексы полезно использовать не только для упорядочивания выборок
записей по какому-либо полю, но и для реализации каких-либо «поисковых
механизмов». Рассмотрим следующий пример (листинг 6.161).
Листинг 6.161. Пример поиска данных в файле DBF
&НаКлиенте
Процедура ПоискДанных(Команда)
Путь = ″c:\temp\″;
БД = Новый XBase;
БД.ОткрытьФайл(Путь + ″list.dbf″, Путь + ″index.cdx″);
БД.ТекущийИндекс = БД.Индексы.IDXCODE;
Сообщение = Новый СообщениеПользователю();
Если БД.Найти(″000000002″, ″=″) Тогда


Пока Истина Цикл

Сообщение.Текст = БД.CODE + ″ ″ + БД.Name;

Сообщение.Сообщить();

Если Не БД.Следующая() Тогда

Прервать;

КонецЕсли;

КонецЦикла;
КонецЕсли;
БД.ЗакрытьФайл();
КонецПроцедуры

В результате будут получены только две записи (листинг 6.162).
Листинг 6.162. Порядок отображения данных
000000002 Смирнова Светлана Ивановна
REST-0003 Артемов Игорь Владимирович

Менять текущий индекс можно и во время выборки (однако следует помнить,
что при «умелой» манипуляции индексами можно никогда не достигнуть
конца DBF-файла).

© ООО «1С-Паблишинг», 2020
© Оформление. ООО «1С-Паблишинг», 2020
Все права защищены.
Материалы предназначены для личного индивидуального использования приобретателем.
Запрещено тиражирование, распространение материалов, предоставление доступа по сети
к материалам без письменного разрешения правообладателей.
Разрешено копирование фрагментов программного кода для использования
в разрабатываемых прикладных решениях.
Фирма «1С»
123056, Москва, а/я 64, Селезневская ул., 21
Тел.: (495) 737-92-57
1c@1c.ru, http://www.1c.ru/
Издательство ООО «1С-Паблишинг»
127434, Москва, Дмитровское ш., 9
Тел.: (495) 681-02-21
publishing@1c.ru, http://books.1c.ru
Об опечатках просьба сообщать по адресу books.v8@1c.ru.