Для выполнения обмена данными добавим в конфигурацию обработку ОбменСУдаленнымиСкладами и откроем ее основную форму.
Добавим в форму реквизит УзелОбмена типа ПланОбменаСсылка.УдаленныеСклады, в котором будет содержаться ссылка на выбранный узел обмена. А также добавим в форму команды ВыгрузитьДанные и ЗагрузитьДанные. Перетащим эти команды и реквизит в окно элементов формы.
У всех кнопок, соответствующих командам, снимем флажок у свойства Доступность. Это делается для того, чтобы действия по выполнению обмена становились возможны только в случае выбора в поле Узел обмена непредопределенного узла, который не совпадает с узлом текущей информационной базы.
Для того чтобы обеспечить такое поведение кнопок, поместим в модуле формы функцию ПолучитьДоступностьПоУзлу(), в которую передается ссылка на выбранный узел (листинг 3.53).
Листинг 3.53. Функция «ПолучитьДоступностьПоУзлу»
&НаСервереБезКонтекста
Функция ПолучитьДоступностьПоУзлу(Узел)
ДоступностьКнопок = Ложь;
// Определить наличие установленного непредопределенного узла.
Если Узел <> ПланыОбмена.УдаленныеСклады.ПустаяСсылка()
И Узел <> ПланыОбмена.УдаленныеСклады.ЭтотУзел()
И Узел.ПолучитьОбъект() <> Неопределено Тогда
ДоступностьКнопок = Истина;
КонецЕсли;
Возврат ДоступностьКнопок;
КонецФункции
Эта функция будет возвращать Истина, в случае если ссылка, содержащаяся в параметре Узел, не пустая и не равна ссылке на предопределенный узел, соответствующий текущей информационной базе. Для получения ссылки на текущий узел используется метод менеджера узла плана обмена ЭтотУзел(). В противном случае функция вернет Ложь.
После этого создадим обработчик события ПриИзменении для поля формы УзелОбмена, в котором доступность кнопок будет зависеть от значения, возвращенного описанной ранее функцией (листинг 3.54).
Листинг 3.54. Обработчик события «ПриИзменении» поля «УзелОбмена»
&НаКлиенте
Процедура УзелОбменаПриИзменении(Элемент)
ДоступностьКнопок = ПолучитьДоступностьПоУзлу(УзелОбмена);
// Установить доступность кнопок в зависимости от установленного узла обмена.
Элементы.ЗагрузитьДанные.Доступность = ДоступностьКнопок;
Элементы.ВыгрузитьДанные.Доступность = ДоступностьКнопок;
КонецПроцедуры
Теперь создадим обработчик команды ВыгрузитьДанные и заполним следующим образом (листинг 3.55).
Листинг 3.55. Обработчик команды «ВыгрузитьДанные»
&НаКлиенте
Процедура ВыполнитьВыгрузку(Команда)
ВыполнитьВыгрузкуНаСервере(УзелОбмена);
КонецПроцедуры
В этом обработчике мы вызываем процедуру ВыполнитьВыгрузкуНаСервере(), в которую передаем ссылку на узел обмена, выбранный в поле обработки. В реквизите УзелОбмена будет содержаться ссылка на узел, для которого будет производиться запись изменений (листинг 3.56).
Листинг 3.56. Процедура «ВыполнитьВыгрузкуНаСервере»
&НаСервереБезКонтекста
Процедура ВыполнитьВыгрузкуНаСервере(Узел)
// Получить объект узла обмена.
УзелОбмена = Узел.ПолучитьОбъект();
// Записать новое сообщение обмена.
УзелОбмена.ЗаписатьСообщениеСИзменениями();
КонецПроцедуры
В этой процедуре мы получаем объект от ссылки на узел плана обмена и выполняем процедуру ЗаписатьСообщенияСИзменениями(), которую мы поместим в модуле плана обмена УдаленныеСклады (см. листинг 3.59).
Теперь создадим обработчик команды ЗагрузитьДанные и заполним следующим образом (листинг 3.57).
Листинг 3.57. Обработчик команды «ЗагрузитьДанные»
&НаКлиенте
Процедура ВыполнитьЗагрузку(Команда)
ВыполнитьЗагрузкуНаСервере(УзелОбмена);
КонецПроцедуры
В этом обработчике мы вызываем процедуру ВыполнитьЗагрузкуНаСервере(), в которую передаем ссылку на узел обмена, выбранный в поле обработки. В реквизите УзелОбмена будет содержаться ссылка на узел, от которого будет производиться чтение изменений (листинг 3.58).
Листинг 3.58. Процедура «ВыполнитьЗагрузкуНаСервере»
&НаСервереБезКонтекста
Процедура ВыполнитьЗагрузкуНаСервере(Узел)
// Получить объект узла обмена.
УзелОбмена = Узел.ПолучитьОбъект();
// Прочитать новое сообщение обмена.
УзелОбмена.ПрочитатьСообщениеСИзменениями();
КонецПроцедуры
В этой процедуре мы получаем объект от ссылки на узел плана обмена и выполняем процедуру ПрочитатьСообщенияСИзменениями(), которую мы поместим в модуле плана обмена УдаленныеСклады (см. листинг 3.61).
Процедуру ЗаписатьСообщенияСИзменениями() в модуле плана обмена заполним следующим образом (листинг 3.59).
Листинг 3.59. Процедура для записи данных обмена
Процедура ЗаписатьСообщениеСИзменениями() Экспорт
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "-------- Выгрузка в узел " + Строка(ЭтотОбъект) + " ------------";
Сообщение.Сообщить();
// Получить имя файла обмена.
ИмяФайла = ОбменСУдаленнымиСкладами.ПолучитьИмяФайлаОбмена(ПланыОбмена.УдаленныеСклады.ЭтотУзел(), Ссылка);
// Создать объект записи XML.
// *** ЗаписьXML-документов.
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяФайла);
ЗаписьXML.ЗаписатьОбъявлениеXML();
// *** Инфраструктура сообщений.
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьСообщения.НачатьЗапись(Запись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).
Листинг 3.60. Функция «ПолучитьИмяФайлаОбмена()»
Функция ПолучитьИмяФайлаОбмена(УзелИсточник, УзелПриемник) Экспорт
// Сформировать полное имя файла обмена.
Каталог = Константы.КаталогОбмена.Получить();
ИмяФайла = Каталог + ?(Прав(Каталог, 1) = "\","", "\") + "Message_" +
СокрЛП(УзелИсточник.Код) + "_" + СокрЛП(УзелПриемник.Код) + ".xml";
Возврат ИмяФайла;
КонецФункции
После этого мы обращаемся к механизмам записи XML-документов и создаем новый объект – ЗаписьXML. С помощью него открываем для записи XML-файл с полученным ранее именем и записываем в него объявление XML.
Затем мы обращаемся к механизмам инфраструктуры сообщений и создаем новый объект ЗаписьСообщенияОбмена, метод которого НачатьЗапись() позволяет, кроме всего прочего, создать очередной номер сообщения и записать заголовок сообщения в XML.
В качестве ссылки на узел обмена, который является получателем сообщения, мы используем значение стандартного реквизита Ссылка плана обмена УдаленныеСклады, в модуле которого мы находимся.
Для сокращения размера файла сообщения первоначально определяем соответствие используемым пространствам имен.
После этого, чтобы получить данные, которые необходимо сохранить в этом файле, мы обращаемся к механизму регистрации изменений и получаем выборку из записей регистрации изменений, предназначенных узлу-получателю. При формировании выборки методом менеджера планов обмена ВыбратьИзменения() мы передаем вторым параметром номер сообщения.
В цикле перебора измененных данных мы сериализуем эти данные в открытый XML-файл методом глобального контекста ЗаписатьXML.
После выхода из цикла мы заканчиваем запись методом ЗакончитьЗапись() объекта ЗаписьСообщенияОбмена. Затем закрываем файл с сообщением обмена методом Закрыть() объекта ЗаписьXML и выводим сообщение об окончании выгрузки данных.
Процедуру ПрочитатьСообщенияСИзменениями() в модуле плана обмена заполним следующим образом (листинг 3.61).
Листинг 3.61. Процедура для чтения данных обмена
Процедура ПрочитатьСообщениеСИзменениями() Экспорт
// Получить имя файла обмена.
ИмяФайла = ОбменСУдаленнымиСкладами.ПолучитьИмяФайлаОбмена(Ссылка, ПланыОбмена.УдаленныеСклады.ЭтотУзел());
Файл = Новый Файл(ИмяФайла);
Если Не Файл.Существует() Тогда
Возврат;
КонецЕсли;
// Попытаться открыть файл.
// *** Чтение документов XML.
ЧтениеXML = Новый ЧтениеXML;
Попытка
ЧтениеXML.ОткрытьФайл(ИмяФайла);
Исключение
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Невозможно открыть файл обмена данными.";
Сообщение.Сообщить();
Возврат;
КонецПопытки;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "-------- Загрузка из " + Строка(ЭтотОбъект) + " ------------";
Сообщение.Сообщить();
Сообщение.Текст = " – Считывается файл " + ИмяФайла;
Сообщение.Сообщить();
// Загрузить данные из найденного файла.
// *** Инфраструктура сообщений.
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
// Прочитать заголовок сообщения обмена данными – файла XML.
ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
// Проверить, что сообщение предназначено для этого узла.
Если ЧтениеСообщения.Отправитель <> Ссылка Тогда
ВызватьИсключение "Неверный узел";
КонецЕсли;
// Удалить регистрацию изменений для узла-отправителя сообщения.
// *** Служба регистрации изменений.
ПланыОбмена.УдалитьРегистрациюИзменений(ЧтениеСообщения.Отправитель, ЧтениеСообщения.НомерПринятого);
// Прочитать данные из сообщения в цикле. *** XML-сериализация.
Пока ВозможностьЧтенияXML(ЧтениеXML) Цикл
// Прочитать очередное значение.
Данные = ПрочитатьXML(ЧтениеXML);
// Записать полученные данные.
Данные.ОбменДанными.Отправитель = ЧтениеСообщения.Отправитель;
Данные.ОбменДанными.Загрузка = Истина;
Данные.Записать();
КонецЦикла;
ЧтениеСообщения.ЗакончитьЧтение();
ЧтениеXML.Закрыть();
УдалитьФайлы(ИмяФайла);
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "-------- Конец загрузки ------------";
Сообщение.Сообщить();
КонецПроцедуры
В этой процедуре с помощью функции ПолучитьИмяФайлаОбмена(), расположенной в общем модуле ОбменСУдаленнымиСкладами (см. листинг 3.60), мы формируем имя файла, из которого будет прочитано сообщение обмена.
Имена файлов сообщений стандартизованы и имеют вид: Message_<КодУзлаОтправителя>_<КодУзлаПолучателя>.xml. В качестве узла-отправителя сообщения обмена в функцию ПолучитьИмяФайлаОбмена() передается ссылка на тот узел обмена, в модуле которого мы находимся, а в качестве узла-получателя – ссылка на предопределенный узел информационной базы.
Затем на основе имени файла мы создаем объект Файл и проверяем, существует ли он. Если файл обмена найден, то мы пытаемся открыть его для чтения с помощью объекта ЧтениеXML. В случае успеха выводим сообщение о начале загрузки данных из файла.
Затем мы обращаемся к механизмам инфраструктуры сообщений планов обмена и создаем объект ЧтениеСообщенияОбмена. Используя метод этого объекта НачатьЧтение(), мы считываем заголовок XML-сообщения, в котором содержится, в том числе, информация об отправителе сообщения.
После этого проверяем, является ли отправитель сообщения тем узлом плана обмена, для которого мы в данном вызове этой процедуры производим обмен данными. В качестве ссылки на узел обмена, который является отправителем сообщения, мы используем значение стандартного реквизита Ссылка плана обмена УдаленныеСклады, в модуле которого мы находимся.
Если все в порядке, то, перед тем как начать чтение данных, мы удаляем все записи регистрации изменений, которые были сделаны для этого узла и соответствовали номерам сообщений, меньшим или равным указанному в обрабатываемом нами сообщении как номер принятого. Это делается затем, чтобы исключить дублирование данных, которые уже были ранее посланы этому узлу и им обработаны. Для этого мы обращаемся к службе регистрации изменений менеджера планов обмена и используем метод УдалитьРегистрациюИзменений().
Затем в цикле мы выполняем чтение данных, содержащихся в сообщении обмена. При этом для каждого элемента в функции ВозможностьЧтенияXML() получается очередной тип данных XML и определяется, имеется ли соответствующий тип «1С:Предприятия».
Для чтения данных мы используем метод глобального контекста ПрочитатьXML(). В результате переменная Данные будет содержать объект «1С:Предприятия», полученный из сообщения обмена.
После этого мы записываем полученные данные методом Записать(). Перед записью полученного объекта мы устанавливаем у него в параметрах обмена данными узел отправителя, чтобы система при записи этого объекта в нашей базе данных не формировала записи регистрации изменений этого объекта для того узла, от которого мы его только что получили.
Кроме того, в параметрах обмена данными мы устанавливаем свойство Загрузка, информирующее систему о том, что запись объекта будет происходить в режиме обновления данных, полученных в результате обмена. Такое указание позволяет системе упростить процедуру записи объекта, отказавшись от ряда стандартных проверок и исключив изменения связанных данных, которые выполняются при обычной записи.
После того как все сообщение обмена будет нами обработано, мы заканчиваем чтение методом ЗакончитьЧтение() объекта ЧтениеСообщенияОбмена. Затем закрываем файл с сообщением обмена методом Закрыть() объекта ЧтениеXML и удаляем этот файл из каталога обмена. В заключение выводим сообщение об окончании загрузки данных из файла.