Также предлагается вариант в виде расширения конфигурации (.cfe) для конфигураций стоящих на поддержке без возможности изменения.
В обмене может участвовать любое количество баз.
Правила обмена подготавливаются с использованием конфигурации «Конвертация данных».
К слову, в БСП 2.3 уже реализован универсальный обмен данными через Веб-сервис, но для его внедрения необходимо включение в конфигурацию всех сопутствующих подсистем БСП, что не всегда представляется возможным. Особенно, если текущая конфигурация на обычных формах (БСП 2.3 ориентировано на управляемые формы).
В представленных алгоритмах были проработаны следующие вопросы:
1. Запуск синхронизации производится в реальном режиме времени при записи (удалении) объекта (документа, справочника), но только если запись выполняется интерактивно. То есть, при пакетном перепроведении документов при восстановлении последовательности или из обработки "Групповая обработка справочников и документов" синхронизация не требуется (предполагается, что движения документов не переносятся, а формируются при проведении).
2. Предусмотрен механизм гарантированной доставки данных в базу приемник. Для этих целей, при запуске синхронизации, объект предварительно регистрируется в плане обмена. В случае сбоя синхронизации по ежеминутному регламентному заданию выполняется проверка наличия зарегистрированных объектов в плане обмена и производится повторная попытка передачи данных в базу приемник. Далее, в случае успешности передачи данных, регистрация объекта в плане обмена удаляется.
3. Для исключения задержек при записи объектов, синхронизация запускается асинхронно в фоновом задании. Если же требуется организовать отказ от проведения документа в текущей базе в зависимости от успешности проведения этого документа в базе приемнике, имеется возможность предусмотреть запуск синхронизации в основном потоке (по опыту, документ реализация товаров в УПП имеющий в табличной части до 10 строчек синхронизируется за 1-2 сек), в этом случае производится ожидание завершения синхронизации и в зависимости от успешности проведения выставляется отказ и дублируется сообщение о причине неуспешности, переданное из базы приемника.
4. Для универсальности данные передаются в базу приемник в формате XML, сформированными программно с помощью обработки "Универсальный обмен данными XML" по правилам обмена, подготовленными в конфигурации "Конвертация данных". Соответственно, на стороне приемника производится загрузка этой же обработкой. Как правило, во всех типовых конфигурациях данная обработка присутствует, а для самописных можно загрузить из состава .cf данной публикации.
5. Пакет передаваемых данных должен иметь минимальный объем данных, Чтобы не загружать сетевые службы и Веб-сервер. Для этих целей, предусмотрено:
— за один раз производится выгрузка только одного объекта (с подчиненными объектами, выгруженными по ссылкам, если это справочник);
— при разработке правил обмена для всех документов устанавливается режим "Не выгружать объекты свойств источника по ссылкам". В этом случае исключается избыточность передаваемых данных. Если же в реквизите встречается ссылка на элемент объекта, который не включен в состав регистрируемых объектов плана обмена (то есть данный вид объектов самостоятельно не синхронизируется) и эта ссылка не была найдена в базе приемнике (в терминах обработки это фиктивная ссылка), то формируется список фиктивных ссылок, который возвращается в базу источник и рекурсивно инициируется синхронизация для этих объектов.
6. Как следствие из предыдущего пункта, проведение документа в базе приемнике реализовано отдельным запросом после полной передачи всего пакета вместе с рекурсивной синхронизацией фиктивных ссылок по предыдущему пункту. Так как для документов отключена выгрузка объектов свойств источника по ссылкам, то на момент первичной записи документа в нем могут присутствовать фиктивные ссылки, из за которых нормальное проведение документа в этот момент невозможно.
Реализация
Процедура ПриОткрытии()
ДополнительныеСвойства.Вставить("СинхронизироватьПриЗаписи");
КонецПроцедуры
Процедура ОбъектПриЗаписиСинхронизировать(Источник, Отказ) Экспорт
ПланОбменаРегистрацияИзменений(ПланыОбмена.ЛУ_WebСервис, Источник, Отказ);
КонецПроцедуры
Процедура ОбъектПередУдалениемСинхронизировать(Источник, Отказ) Экспорт
ПланОбменаРегистрацияИзменений(ПланыОбмена.ЛУ_WebСервис, Источник, Отказ, Истина);
КонецПроцедуры
Процедура ПланОбменаРегистрацияИзменений(ПланОбмена, Источник, Отказ, Удаление=Ложь) Экспорт
#Если НЕ ТолстыйКлиентОбычноеПриложение Тогда
Возврат;
#КонецЕсли
Если Отказ или Источник.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Если НЕ (Источник.ДополнительныеСвойства.Свойство("СинхронизироватьПриЗаписи") или Удаление) Тогда
Возврат;
КонецЕсли;
ПланОбменаМетаданные = ПланОбмена.ПустаяСсылка().Метаданные();
Если НЕ ПланОбменаМетаданные.Состав.Содержит(Источник.Метаданные()) Тогда
Возврат;
КонецЕсли;
// Регистрация изменениий в плане обмена
ВыборкаУзлов = ПланОбмена.Выбрать();
Пока ВыборкаУзлов.Следующий() Цикл
ТекущийУзел = ВыборкаУзлов.Ссылка;
Если ТекущийУзел = ПланОбмена.ЭтотУзел() Тогда
Продолжить;
КонецЕсли;
Если НЕ ТекущийУзел.Синхронизировать Тогда
Продолжить;
КонецЕсли;
ОбъектРегистрации = ?(Удаление, Новый УдалениеОбъекта(Источник.Ссылка), Источник.Ссылка);
ПланыОбмена.ЗарегистрироватьИзменения(ТекущийУзел, ОбъектРегистрации);
КонецЦикла;
ЛУ_СинхронизацияВызовСервера.ПланОбменаСинхронизироватьАсинхронно(ПланОбменаМетаданные.Имя);
КонецПроцедуры
Здесь:
- Синхронизировать — булево,
- Сервер, База, Пользователь, Пароль — Строка, 100, переменная
- ПравилаКонвертации — ХранилищеЗначения (в форме узла реализована загрузка в этот реквизит правил обмена из файла XML)
Процедура ПланОбменаСинхронизировать(ИмяПланаОбмена) Экспорт
ПланОбмена = ПланыОбмена[ИмяПланаОбмена];
ВыборкаУзлов = ПланОбмена.Выбрать();
Пока ВыборкаУзлов.Следующий() Цикл
ТекущийУзел = ВыборкаУзлов.Ссылка;
Если ТекущийУзел = ПланОбмена.ЭтотУзел() Тогда
Продолжить;
КонецЕсли;
Если НЕ ТекущийУзел.Синхронизировать Тогда
Продолжить;
КонецЕсли;
ИмяСобытияДляЖурнала = "ПланОбмена." + ИмяПланаОбмена + " " + ПланОбмена.ЭтотУзел().Код + " -> " + ТекущийУзел.Код;
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьСообщенияXML = Новый ЗаписьXML;
ЗаписьСообщенияXML.УстановитьСтроку();
Попытка
ЗаписьСообщения.НачатьЗапись(ЗаписьСообщенияXML, ТекущийУзел);
Исключение
// ТекущийУзел заблокирован, пока пропустим, следующая попытка будет по регламентному заданию через 60 сек
Продолжить;
КонецПопытки;
НомерСообщения = ЗаписьСообщения.НомерСообщения;
МетаданныеДокументы = Метаданные.Документы;
МассивУдаляемыхОбъектов = Новый Массив;
МассивОбъектов = Новый Массив;
Выборка = ПланыОбмена.ВыбратьИзменения(ТекущийУзел, НомерСообщения);
Пока Выборка.Следующий() Цикл
// В этом цикле только подготовим массив объектов, содержащихся в сообщении плана обмена
// Чтобы побыстрее освободить ЗаписьСообщения
// (до вызова метода ЗакончитьЗапись() в плане обмена устанавливается исключительная блокировка)
Объект = Выборка.Получить();
Если ТипЗнч(Объект) = Тип("УдалениеОбъекта") Тогда
МассивУдаляемыхОбъектов.Добавить(Объект);
ИначеЕсли НЕ МетаданныеДокументы.Содержит(Объект.Ссылка.Метаданные()) Тогда
МассивОбъектов.Вставить(0, Объект.Ссылка); // Документы сдвигаем в конец массива
Иначе
МассивОбъектов.Добавить(Объект.Ссылка);
КонецЕсли;
КонецЦикла;
Если МассивОбъектов.Количество() = 0 и МассивУдаляемыхОбъектов.Количество() = 0 Тогда
ЗаписьСообщения.ПрерватьЗапись(); // Освобождаем номер сообщения плана обмена
Продолжить;
КонецЕсли;
ЗаписьСообщения.ЗакончитьЗапись(); // Фиксируем исходящее сообщение плана обмена данных
// Полученный XML сообщения игнорируем.
// Так как в базе приемнике фиксация номеров входящих сообщений плана обмена не предусмотрена
// (В приемнике вообще может не быть корреспондирующего плана обмена при одностороннем обмене).
ЗаписьСообщенияXML.Закрыть();
// Передача измененных объектов в базу приемник
Отказ = Ложь;
ОбработкаОбмена = ИнициализироватьОбработкуОбмена(ТекущийУзел, ИмяСобытияДляЖурнала, Отказ);
ФайлОбмена = Новый Файл(ОбработкаОбмена.ИмяФайлаОбмена);
// Подключение
LuExchange = ПолучитьWSПрокси(ТекущийУзел, "LuExchange.1cws", "http://www.LuExchange.ru", "LuExchange");
// Удаляемые объекты // Поиск удаляемого объекта выполняется только по ссылке
ВыгрузитьУдаляемыеОбъектыВБазуПриемник(ТекущийУзел, МассивУдаляемыхОбъектов, LuExchange, ОбработкаОбмена, ФайлОбмена, ИмяСобытияДляЖурнала, Отказ);
// Измененные объекты
Для каждого ОбъектСсылка Из МассивОбъектов Цикл
ОшибкаВыгрузкиОбъекта = Ложь;
СтрокаЗагруженныеДокументы = ""; // Массив загруженных документов в базе приемнике (строка внутренняя) для инициации последующего проведения
ВыгрузитьОбъектВБазуПриемник(ОбъектСсылка, LuExchange, ОбработкаОбмена, ФайлОбмена, ИмяСобытияДляЖурнала, СтрокаЗагруженныеДокументы, ОшибкаВыгрузкиОбъекта);
Если ОшибкаВыгрузкиОбъекта Тогда
Отказ = Истина;
Продолжить;
КонецЕсли;
// Для выгруженных документов необходимо выполнить команду проведения или, если документ был распроведен, удалить движения
ОшибкаПроведенияДокумента = Ложь;
ОбработкаПризнакаПроведенияДокумента(СтрокаЗагруженныеДокументы, LuExchange, ИмяСобытияДляЖурнала, ОшибкаПроведенияДокумента);
// Фиксируем успешную синхронизацию в плане обмена для объекта
ПланыОбмена.УдалитьРегистрациюИзменений(ТекущийУзел, ОбъектСсылка);
КонецЦикла;
УдалитьФайлы(ОбработкаОбмена.ИмяФайлаОбмена);
Если НЕ Отказ Тогда
// Фиксируем успешную синхронизацию в плане обмена для всего сообщения
ПланыОбмена.УдалитьРегистрациюИзменений(ТекущийУзел, НомерСообщения);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Процедура ВыгрузитьОбъектВБазуПриемник(ОбъектСсылка, LuExchange, ОбработкаОбмена, ФайлОбмена, ИмяСобытияДляЖурнала, СтрокаЗагруженныеДокументы="", Отказ)
// Получаем ДанныеXML
ВыгруженоОбъектов = 0;
ДанныеXML = ПолучитьДанныеXMLПоПравиламКонвертации(ОбработкаОбмена, ОбъектСсылка, ВыгруженоОбъектов, ИмяСобытияДляЖурнала);
// ExecuteExchange
ТекстОшибки = "";
ОшибкаВыгрузки = Ложь;
СтрокаФиктивныеСсылки = "";
Данные = Новый ХранилищеЗначения(ДанныеXML, Новый СжатиеДанных(9));
ЗагруженоОбъектов = LuExchange.ExecuteExchange(Данные, ИмяСобытияДляЖурнала, СтрокаФиктивныеСсылки, СтрокаЗагруженныеДокументы, ТекстОшибки);
// Фиктивные ссылки (выгружаем объекты по ссылкам)
ОбработкаФиктивныхСсылок(СтрокаФиктивныеСсылки, LuExchange, ОбработкаОбмена, ФайлОбмена, ИмяСобытияДляЖурнала, ОшибкаВыгрузки);
КонецПроцедуры
Процедура ОбработкаФиктивныхСсылок(СтрокаФиктивныеСсылки, LuExchange, ОбработкаОбмена, ФайлОбмена, ИмяСобытияДляЖурнала, Отказ)
Если ПустаяСтрока(СтрокаФиктивныеСсылки) Тогда
Возврат;
КонецЕсли;
ФиктивныеСсылки = ЗначениеИзСтрокиВнутр(СтрокаФиктивныеСсылки);
Для каждого ФиктивнаяСсылка Из ФиктивныеСсылки Цикл
ФиктивнаяСсылка = СтрЗаменить(ФиктивнаяСсылка, ".", Символы.ПС);
ВидПриемника = СтрПолучитьСтроку(ФиктивнаяСсылка, 1);
ТипПриемника = СтрПолучитьСтроку(ФиктивнаяСсылка, 2);
УИД = СтрПолучитьСтроку(ФиктивнаяСсылка, 3);
// Здесь тип объекта соответствует метаданным базы приемника,
// для обработки необходимо привести к соответствующему типу текущей базы
// Найдем корреспондирующий тип по правилам обмена
ПравилоКонвертации = ОбработкаОбмена.ТаблицаПравилКонвертации.Найти(ВидПриемника+"Ссылка."+ТипПриемника, "Приемник");
Если ПравилоКонвертации = Неопределено Тогда
Продолжить;
КонецЕсли;
TypeOf = СтрЗаменить(ПравилоКонвертации.ТипИсточника, ".", Символы.ПС);
ВидИсточника = СтрПолучитьСтроку(TypeOf, 1);
ТипИсточника = СтрПолучитьСтроку(TypeOf, 2);
Если ВидИсточника = "ДокументСсылка" Тогда
МенеджерОбъекта = Документы[ТипИсточника];
ИначеЕсли ВидИсточника = "СправочникСсылка" Тогда
МенеджерОбъекта = Справочники[ТипИсточника];
Иначе
Отказ = Истина;
Продолжить;
КонецЕсли;
ОбъектСсылка = МенеджерОбъекта.ПолучитьСсылку(Новый УникальныйИдентификатор(УИД));
НайденныйОбъект = ОбъектСсылка.ПолучитьОбъект();
Если НайденныйОбъект = Неопределено Тогда
Отказ = Истина;
Продолжить;
КонецЕсли;
ВыгрузитьОбъектВБазуПриемник(ОбъектСсылка, LuExchange, ОбработкаОбмена, ФайлОбмена, ИмяСобытияДляЖурнала,, Отказ);
КонецЦикла;
КонецПроцедуры
Процедура ОбработкаПризнакаПроведенияДокумента(СтрокаЗагруженныеДокументы, LuExchange, ИмяСобытияДляЖурнала, Отказ)
Если ПустаяСтрока(СтрокаЗагруженныеДокументы) Тогда
Возврат;
КонецЕсли;
// PostingDocuments
ТекстОшибки = "";
Отказ = LuExchange.PostingDocuments(СтрокаЗагруженныеДокументы, ТекстОшибки);
КонецПроцедуры
Функция ИнициализироватьОбработкуОбмена(ТекущийУзел, ИмяСобытияДляЖурнала, Отказ)
// Инициализация
ОбработкаОбмена = Обработки.УниверсальныйОбменДаннымиXML.Создать();
ОбработкаОбмена.РежимОбмена = "Выгрузка";
ОбработкаОбмена.ПостроительОтчета = Новый ПостроительОтчета;
ОбработкаОбмена.НеВыводитьНикакихИнформационныхСообщенийПользователю = Истина;
// Загрузка правил
ПравилаОбмена = ТекущийУзел.ПравилаКонвертации.Получить();
ОбработкаОбмена.ЗагрузитьПравилаОбмена(ПравилаОбмена, "Строка");
ОбработкаОбмена.ИмяФайлаПравилОбмена = "Неопределено"; // Обходим глюк обработки, иначе она считает, что правила не загружены
// ИмяФайлаОбмена
ОбработкаОбмена.ИмяФайлаОбмена = ПолучитьИмяВременногоФайла("xml");
// Для всех правил конвертации справочников и документов рекомендуется по возможности устанавливать в ИСТИНА следующие свойства:
// - Искать объект приемника по внутреннему идентификатору объекта источника;
// - При переносе объкта по ссылке НЕ создавать новый объект, а только переносить ссылку;
// Для правил конвертации документов дополнительно рекомендуется устанавливать в ИСТИНА следующее свойство:
// - Не выгружать объекты свойств источника по ссылкам;
// В этом случае файл данных XML для выгружаемого объекта не будет содержать объекты выгружаемые по ссылкам.
// Это существенно уменьшит размер передаваемого файла и ускорит процесс синхронизации.
// Выгрузка этих объектов при необходимости будет инициироваться автоматически.
Возврат ОбработкаОбмена;
КонецФункции
Функция ПолучитьДанныеXMLПоПравиламКонвертации(ОбработкаОбмена, ОбъектСсылка, ВыгруженоОбъектов, ИмяСобытияДляЖурнала)
МетаИмя = ОбъектСсылка.Метаданные().Имя;
// Сначала все отключаем
Для Каждого Строка из ОбработкаОбмена.ТаблицаПравилВыгрузки.Строки Цикл
Строка.Включить = 0;
ОбработкаОбмена.УстановитьПометкиПодчиненных(Строка, "Включить");
КонецЦикла;
// Включаем нужное правило
СтрПравил = ОбработкаОбмена.ТаблицаПравилВыгрузки.Строки.Найти(МетаИмя, "Имя", Истина);
Если СтрПравил = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
Если НЕ ЗначениеЗаполнено(СтрПравил.ИмяОбъектаДляЗапроса) Тогда
Возврат Неопределено;
КонецЕсли;
СтрПравил.Включить = 1;
ОбработкаОбмена.УстановитьПометкиРодителей(СтрПравил, "Включить");
// Отбор
ИмяОтбора = СтрЗаменить(СтрПравил.ИмяОбъектаДляЗапроса, ".", "_");
ПостроительОтчета = Новый ПостроительОтчета("ВЫБРАТЬ ПЕРВЫЕ 1 _.* ИЗ " + СтрПравил.ИмяОбъектаДляЗапроса + " КАК _ {ГДЕ _.Ссылка.* КАК " + ИмяОтбора + "}");
Отбор = ПостроительОтчета.Отбор.Добавить(ИмяОтбора);
Отбор.ВидСравнения = ВидСравнения.Равно;
Отбор.Установить(ОбъектСсылка);
СтрПравил.ИспользоватьОтбор = Истина;
СтрПравил.НастройкиПостроителя = ПостроительОтчета.ПолучитьНастройки();
// Выгрузка
ОбработкаОбмена.ВыполнитьВыгрузку();
ВыгруженоОбъектов = ОбработкаОбмена.мСчетчикВыгруженныхОбъектов;
ЧтениеТекста = Новый ЧтениеТекста;
ЧтениеТекста.Открыть(ОбработкаОбмена.ИмяФайлаОбмена, КодировкаТекста.UTF8);
СтрокаXML = ЧтениеТекста.Прочитать();
ЧтениеТекста.Закрыть();
Возврат СтрокаXML;
КонецФункции // ПолучитьДанныеXMLПоПравиламКонвертации()
Функция ПолучитьWSПрокси(Параметры, ИмяФайлаПубликации, URIПространстваИменСервиса, ИмяСервиса)
Определения = Новый WSОпределения("http://"+Параметры.Сервер+"/"+Параметры.База+"/ws/"+ИмяФайлаПубликации+"?wsdl", Параметры.Пользователь, Параметры.Пароль);
WSПрокси = Новый WSПрокси(Определения, URIПространстваИменСервиса, ИмяСервиса, ИмяСервиса+"Soap");
WSПрокси.Пользователь = Параметры.Пользователь;
WSПрокси.Пароль = Параметры.Пароль;
Возврат WSПрокси;
КонецФункции
Функция ExecuteExchange(DataXML, NameOfEvent, FictitiousObjects, UploadedDocuments, ErrorMessage)
УстановитьПривилегированныйРежим(Истина);
Попытка
ОбработкаОбмена = Обработки.УниверсальныйОбменДаннымиXML.Создать();
ОбработкаОбмена.РежимОбмена = "Загрузка";
ОбработкаОбмена.НеВыводитьНикакихИнформационныхСообщенийПользователю = Истина;
ОбработкаОбмена.ЗагружатьДанныеВРежимеОбмена = Истина;
ОбработкаОбмена.ЗаписыватьВИнформационнуюБазуТолькоИзмененныеОбъекты = Истина;
ОбработкаОбмена.ОптимизированнаяЗаписьОбъектов = Истина;
ОбработкаОбмена.ЗаписыватьРегистрыНаборамиЗаписей = Истина;
РаботаВозможна = ОбработкаОбмена.ВыполнитьДействияПередЧтениемДанных(DataXML.Получить());
Если НЕ РаботаВозможна Тогда
ErrorMessage = "ExecuteExchange(1). ОбработкаОбмена.ВыполнитьДействияПередЧтениемДанных - работа не возможна";
Возврат 0;
КонецЕсли;
ОбработкаОбмена.ПроизвестиЧтениеДанных(ErrorMessage);
Если НЕ ПустаяСтрока(ErrorMessage) Тогда
Возврат 0;
КонецЕсли;
ОбработкаОбмена.ВыполнитьДействияПослеЗавершенияЧтенияДанных();
// Подготовим для возврата в базу источник массивы Фиктивных ссылок и массив выгруженных документов
МассивФиктивныхСсылок = Новый Массив;
МассивЗагруженныхДокументов = Новый Массив;
МетаданныеДокументы = Метаданные.Документы;
Для каждого КлючИЗначение Из ОбработкаОбмена.ЗагруженныеОбъекты Цикл
ОбъектСсылка = КлючИЗначение.Значение.СсылкаНаОбъект;
ОбъектМетаданные = ОбъектСсылка.Метаданные();
Если КлючИЗначение.Значение.СсылкаФиктивная Тогда
// Фиктивная ссылка - объект не найденный в базе приемнике и не выгруженный по ссылкам
МассивФиктивныхСсылок.Добавить(ОбъектМетаданные.ПолноеИмя() + "." + XMLСтрока(ОбъектСсылка.Ссылка));
ИначеЕсли МетаданныеДокументы.Содержит(ОбъектМетаданные) Тогда
МассивЗагруженныхДокументов.Добавить(ОбъектСсылка.Ссылка);
КонецЕсли;
КонецЦикла;
Если МассивФиктивныхСсылок.Количество() > 0 Тогда
FictitiousObjects = ЗначениеВСтрокуВнутр(МассивФиктивныхСсылок);
КонецЕсли;
Если МассивЗагруженныхДокументов.Количество() > 0 Тогда
UploadedDocuments = ЗначениеВСтрокуВнутр(МассивЗагруженныхДокументов);
КонецЕсли;
Исключение
ErrorMessage = "ExecuteExchange(2). " + ОписаниеОшибки();
Возврат 0;
КонецПопытки;
Возврат ОбработкаОбмена.мСчетчикЗагруженныхОбъектов;
КонецФункции
Функция DeleteObject(TypeOf, UUID, ErrorMessage)
УстановитьПривилегированныйРежим(Истина);
Попытка
TypeOf = СтрЗаменить(TypeOf, ".", Символы.ПС);
ВидОбъекта = СтрПолучитьСтроку(TypeOf, 1);
ТипОбъекта = СтрПолучитьСтроку(TypeOf, 2);
Если ВидОбъекта = "ДокументСсылка" Тогда
МенеджерОбъекта = Документы[ТипОбъекта];
ИначеЕсли ВидОбъекта = "СправочникСсылка" Тогда
МенеджерОбъекта = Справочники[ТипОбъекта];
Иначе
ErrorMessage = "DeleteObject(1). Ошибка получения менеджера объекта " + ВидОбъекта + "." + ТипОбъекта + "!";
Возврат Истина;
КонецЕсли;
ОбъектСсылка = МенеджерОбъекта.ПолучитьСсылку(Новый УникальныйИдентификатор(UUID));
НайденныйОбъект = ОбъектСсылка.ПолучитьОбъект();
Если НЕ НайденныйОбъект = Неопределено Тогда
НайденныйОбъект.Удалить();
// В базе приемнике удаление происходит без проверки ссылочной целостности
// В некоторых схемах целесообразнее отключать данный функционал
// а удаление выполнять соответствующей обработкой
// ("ПометкаУдаления" синхронизируется обычным образом в ExecuteExchange)
КонецЕсли;
Исключение
ErrorMessage = "DeleteObject(2). " + ОписаниеОшибки();
Возврат Истина;
КонецПопытки;
Возврат Ложь;
КонецФункции
Функция PostingDocuments(UploadedDocuments, ErrorMessage)
УстановитьПривилегированныйРежим(Истина);
Отказ = Ложь;
Попытка
МассивВыгруженныхДокументов = ЗначениеИзСтрокиВнутр(UploadedDocuments);
Для каждого ДокументСсылка Из МассивВыгруженныхДокументов Цикл
ДокументОбъект = ДокументСсылка.ПолучитьОбъект();
Если ДокументОбъект = Неопределено Тогда
Отказ = Истина;
ErrorMessage = ErrorMessage + Символы.ПС + "PostingDocuments(1). Объект не найден: " + ДокументСсылка;
Продолжить;
КонецЕсли;
// Проведение
Если ДокументОбъект.Проведен Тогда
Попытка
ПолучитьСообщенияПользователю(Истина); // Очистим все предыдущие сообщения на момент проведения
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
Сообщения = ПолучитьСообщенияПользователю(Истина); // Вернем в базу источник ошибки проведения
ТекстСообщения = "";
Для каждого Сообщение Из Сообщения Цикл
ТекстСообщения = ТекстСообщения + Символы.ПС + Сообщение.Текст;
КонецЦикла;
ErrorMessage = ErrorMessage + Символы.ПС + "PostingDocuments(2). " + ДокументСсылка + Символы.ПС + ОписаниеОшибки() + ТекстСообщения;
Отказ = Истина;
// Сбросим признак проведения
ДокументОбъект.Проведен = Ложь;
ДокументОбъект.ОбменДанными.Загрузка = Истина;
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецПопытки;
КонецЕсли;
// Если снят с проведения, очистим движения
Если НЕ ДокументОбъект.Проведен Тогда
Для каждого НаборДвижений Из ДокументОбъект.Движения Цикл
НаборДвижений.Прочитать();
Если НаборДвижений.Количество() > 0 Тогда
НаборДвижений.Очистить();
НаборДвижений.Записать();
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
Исключение
ErrorMessage = "PostingDocuments(3). " + ОписаниеОшибки();
Отказ = Истина;
КонецПопытки;
Возврат Отказ;
КонецФункции
Данная схема успешно реализована в реальных действующих системах:
- связка УТ 10.3 и УПП 1.3. (УТ нетиповая, доработана под производство с вводом данных через тач-панели на конвейерной линии)
- связка Самописная конфигурация и БП 3.0 (В самописной конфигурации ведется учет технологических операций на фабрике птицы)
- ОбменДаннымиОнлайнЧерезВебСервис.cf — содержит все описанные объекты для синхронизации (Источник + Приемник). Обычные формы. Клиент-серверный вариант. В файловом варианте тоже все будет работать, кроме регламентного задания. В этом случае необходимо предусмотреть периодический запуск синхронизации с помощью метода ПодключитьОбработчикОжидания(). При сравнении, объединении необходимо отметить по подсистемам файла: подсистема "ЛУ_СинхронизацияЧерезВебСервис".
- ДемоБазы.zip — содержит выгрузки из двух баз (PR1.dt и PR2.dt) для демонстрации готовой настроенной схемы синхронизации. Базы содержат простенькие документы и справочники. Имена метаданных синхронизируемых объектов различаются. Правила конвертации загружены в базу. Необходимо восстановить базы из .dt. Выполнить публикацию на Веб-сервере под именами PR1 и PR2 с установленной галочкой Публиковать Веб-сервисы и LuExchange. В режиме Предприятия в настройках соответствующих узлов планов обмена необходимо прописать имя Веб-сервера на котором была выполнена публикация баз.
- ОбменДаннымиОнлайнЧерезВебСервис.cfe — вариант доработки в виде расширения конфигурации для баз находящихся на поддержке без возможности редактирования. Ввиду ограниченного функционала расширений, не содержит объекты: План обмена и Регламентное задание. То есть синхронизация здесь реализована без квитирования успешности доставки данных, но полностью совместима с протоколом обмена основного .cf
11. Инструкция по внедрению (ОбменДаннымиОнлайнЧерезВебСервис.cf)
Скачиваем файл ОбменДаннымиОнлайнЧерезВебСервис.cf
При публикации отмечаем галочками Публиковать Веб-сервисы и LuExchange.
- Сервер — имя веб-сервера, на котором выполнена публикация баз
- База — имя, под которым выполнена публикация корреспондирующей базы на веб-сервере, как правило совпадает с именем базы на сервере 1С Предприятие
- Логин — имя пользователя, которого необходимо создать в обеих базах с ролью WebСервисLuExchange (подключение веб-сервиса будет происходить под этим пользователем)
- Пароль
- Правила конвертации — сюда необходимо загрузить правила конвертации, созданные в конфигурации "Конвертация данных"
- Для ПВД поддерживается только способ выборки: "Стандартная выборка"
- Для всех ПКО справочников и документов рекомендуется устанавливать в ИСТИНА следующие свойства:
- Искать объект приемника по внутреннему идентификатору объекта источника;
- При переносе объкта по ссылке НЕ создавать новый объект, а только переносить ссылку;
- Для ПКО документов дополнительно рекомендуется устанавливать в ИСТИНА следующее свойство:
- Не выгружать объекты свойств источника по ссылкам;
В этом случае файл данных XML для выгружаемого объекта не будет содержать объекты выгружаемые по ссылкам. Это существенно уменьшит размер передаваемых данных и ускорит процесс синхронизации. Выгрузка этих объектов при необходимости будет инициироваться автоматически.
можно скачать вложение "ДемоБазы.zip" и посмотреть как это выполнено в них.
А какой объём данных можно передать за одно подключение?
У меня бывает пакеты по 30-40 Мб. На плохих каналах связи не всегда через ftp удаётся передать такой объём.
(1) TODD22,
У меня бывает пакеты по 30-40 Мб. На плохих каналах связи не всегда через ftp удаётся передать такой объём.
В представленной публикации алгоритм реализован именно для минимизации размера передаваемых данных через Веб-сервис.
Для этого:
— В одном пакете передается только один объект (элемент справочника или документ)
— Для документов в правилах конвертации отключена выгрузка объектов по ссылкам. Эта настройка существенно уменьшает размер передаваемых данных, исключая их избыточность. В случае необходимости, если подчиненный объект все же отсутствует в базе приемнике, то автоматически рекурсивно запускается его выгрузка в новых пакетах.
В итоге максимальный размер одного пакета в реальной схеме при синхронизации УПП 1.3 — УТ 10.3 для документа Поступление товаров и услуг (в среднем 10-20 строчек в табличной части) не превышает 0.5 Мб.
При стандартной установке для IIS максимальный размер обрабатываемого сообщения ~30 МБ
Если в Вашей схеме вес одного пакета зашкаливает за 30 Мб., то тогда Вам в помощь эта публикация:Передача больших пакетов через веб-сервисы
Интересно.
Приобрел оба файла. не жалею. все работает. спасибо ,нужная вещь . советую
Зачем сервис для этого? Какие задачи это решает?
(5)
Можно организовать онлайн обмен и без Веб-сервиса (в некоторых случаях), например используя COM-объект.
Здесь можно отметить преимущества и недостатки синхронизации через Веб-сервис в отличии от COM-объекта:
Недостатки COM-объекта:
1. Подключиться к базе через COM-объект вне локальной сети невозможно (хотя, если извратиться, то можно, но в любом случае, это вызовет хождение колоссального трафика). Через Веб-сервис можно легко обмениваться данными через интернет. В этом случае для безопасности необходимо подключаться используя SSL.
2. COM-объект — тяжелая штука. Ресурсы жрет немерено. Если у Вас работает несколько сотрудников, то скорее Вы этого не ощутите, а вот если 100-200 и все начнут что-то онлайн синхронить….
3. Для запуска COM-объекта требуется некоторое время.
4. Для COM-объекта обязательным условием для подключения является точное соответствие версии платформы базы Источника и базы Приемника. То есть, если Ваши базы находятся на разных серверах, обновление платформы 1С на обоих серверах необходимо производить одновременно!
Недостатки Веб-сервиса:
1. Стартует Веб-сервис быстро, даже при холодном старте, но пропускная способность http оставляет желать лучшего.
2. К недостаткам Веб-сервиса так же можно отнести необходимость заботиться о размере передаваемых данных, при стандартной настройке Веб-сервера IIS размер одного пакета данных не должен превышать 30Мб.
Разрабатываю похожий сервис, но несколько иного характера. Нет необходимости синхронизации «на лету». Столкнулся с проблемой подключения к веб-сервису с использованием шифрования. На клиенте все хорошо, 1с видит сертификат, считает ресурс надежным. На сервере же что только не перепробовал — «Удаленный узел не прошел проверку» и все. Хоть убей. Платформа 8.3.9.1818.
— добавлял сертификат в cacert.pem
— добавлял сертификат в доверенный центры
— подсовывал файл сертификата в ЗащищенноеСоединениеOpenSSL
— отключал проверку сертификата в ЗащищенноеСоединениеOpenSSL
Если зайти на сервер, открыть браузер, перейти по ссылке веб-сервиса, то браузер покажет что узел надежный.
(7) Возможно при выпуске клиентского сертификата вы не указали политику применения — подтверждать подлинность клиента.
Если Вам не нужна проверка подлинности клиента, то в настройках Веб-сервера укажите — сертификат клиента игнорировать.
(8) я не выпускал клиентский сертификат. Был создан самоподписанный сертификат для сервера, и добавлен в системный список доверенных центров сертификации. И проблема именно в том, что сервер 1С не видит, что этот сертификат в доверенных, и, соответственно, не может доверять узлу, на котором расположен web-сервис.
В моем случае все работало с использованием следующего кода, но сертификат был не самоподписанный:
(10) Если сертификат имеет подпись поверенного центра сертификации, строка: «ssl = Новый ЗащищенноеСоединениеOpenSSL()» вообще не требуется.
Кстати, по заявлению 1С, в 8.3.9 внесенго исправление, согласно которому, если ЗащищенноеСоединениеOpenSSL создать без указания хранилища серверных сертификатов, то проверка сертификата вообще не должна выполнятся. Однако выполняется.
Не помню на какой версии платформы, это было в 2015 году, но только так и работало.
Возможно с тех пор все поменялось…
Можно ли настроить обмен, чтобы опубликовать только одну базу?
Можно, соответственно, данные будут синхронизироваться только в одну сторону.
но подтверждение о доставке тогда не будет работать.
Наемному бухгалтеру на компьютере ведь не будешь веб-сервер устанавливать.
Придется мучить стандартный Прочий обмен данными и обмен через ftp.
(15)
Почему не будет? Будет. Алгоритм текущей доработки следующий:
При односторонней синхронизации на Веб-сервере публикуется только база-приемник.
База источник отправляет данные в базу приемник и не завершая сессию ждет ответ.
Если ответ положительный, то в плане обмена удаляется регистрация переданного объекта,
иначе не удаляется и при следующем запуске обмена повторяется попытка передачи этого объекта.
Но можно и наоборот, см.ниже.
Придется мучить стандартный Прочий обмен данными и обмен через ftp.
Для Вашего случая можно предложить переделать алгоритм следующим образом:
На веб-сервере публикуем только базу-источник.
В этом случае периферийная база должна выступать в роли инициатора обмена,
То есть, по инициативе пользователя или по регл.заданию выполняется запрос в базу-источник,
в ответ на который сервер должен передать данные очередного объекта.
Объект записывается в текущей базе, на сервер передается квитанция об успешности записи
и запрашивается очередной объект. До тех пор, пока сервер не ответит, что все объекты синхронизированы.
Этот вариант можно реализовать самостоятельно, используя те же методы получения данных объекта в формате XML
и обратного преобразования, описанных в публикации.
Не забудьте проконтролировать «фиктивные ссылки» и дополнительно запросить их данные с сервера.
К сожалению не смог применить к своей задаче данную разработку.
1. Если не выгружать подчиненные по ссылке, то база-приемник запросит недостающие объекты, но в будущем, если в источнике изменят например номенклатуру, то изменения не попадут в приемник.
2. Изменения в нужных для обмена документах происходят не только интерактивно, но и программно, например пакетами загружаются с сайта. Запускать обмен при получении каждого документа невозможно.
Еще в коде непонятно, чем отличается
от
ведь в первом случае все равно регистрируется просто ссылка из УдалениеОбъекта
«Но с данным кодом возникнет следующая проблема – при наличии строки Удаление.Записать(); будет удален объект в текущей базе (а это не нужно). При отсутствии строки Удаление.Записать(); в удаленной базе данные останутся, то есть задача не будет решена.»
https://infostart.ru/public/188547/
(17)
Смотри внимательнее подписки:
Источник — СправочникОбъект, ДокументОбъект
то есть, при изменении Номенклатуры, так же запуститься синхронизация и данный элемент будет синхронизирован!
В данных алгоритмах целенаправленно была реализована выборочная регистрация измененных объектов в плане обмена только при интерактивной записи (по условиям задания).
В Вашем же случае все гораздо проще: убираем соответствующие подписки для регистрации объектов, активируем автоматическую регистрацию в плане обмена типовыми средствами, а после загрузки данных с сайта вызываем синхронизацию процедурой:
либо вешаем этот метод на регламентное задание с нужной периодичностью.
Опять, читаем код более внимательно:
Объект УдалениеОбъекта в данном случае нужен исключительно для того, чтобы далее, при чтении изменений из плана обмена для отправки в кор. базу отличать измененные объекты от удаленных,
соответственно массив удаленных объектов отправляется в кор.базу собственным методом,
см.
Далее:
Ну, и на стороне базы приемника:
В этом коде МассивУдаляемыхОбъектов будет всегда пустым, так как УдалениеОбъекта без выполненного метода Записать попадает в УзелОбмена как ссылка на измененный объект, а не как объект УдалениеОбъекта.
Я предположил, что раз есть механизм фиктивных ссылок, то включать подчиненные справочники в состав плана обмена не нужно. Ведь если регистрировать все подчиненные справочники, то механизм фиктивных ссылок становится не нужен.
Лучше без этого
(19)
— Согласен, действительно получилось не очень корректно.
Исправлюсь)
Но по факту — не пустой!)
Метод Записать(), инициирующий удаление объекта, в данном случае не используется, так как код и так выполняется в подписке ПередУдалением.
Здесь важно, чтобы на момент чтения изменений из плана обмена, объект уже был удален. Это достигается тем, что соответствующий участок кода выполняется асинхронно в фоновом задании, которое стартует с некоторой задержкой, после того, как отработает подписка ПередУдалением и транзакция удаления объекта уже завершилась.
Тут главное отличие механизма Фиктивных ссылок (с регистрацией всех подчиненных справочников) от выгрузки По ссылкам в том, что
в первом случае элементы справочников синхронизируются только при изменении, либо отсутствии подчиненной ссылки в базе назначения (фиктивная ссылка),
Этим как раз и достигается основной эффект уменьшения размера пакетов передаваемых данных и существенного увеличения скорости обмена.
во втором случае (при выгрузке по ссылкам) каждый раз при выгрузке документа вместе с ним выгружаются все подчиненные справочники.
(20)
Интересная комбинация. Протестировать затратно по времени, но раз работает, то работает.
(20)
Я предполагал третий вариант. Первичная выгрузка документов делается с выгрузкой подчиненных объектов по ссылкам, потом выгрузка подчиненных по ссылкам у документов отключается и подчиненные выгружаются только при их изменении и попадании в регистрацию узла обмена.
В таком случае и файл обмена небольшой и дополнительный механизм фиктивных ссылок не нужен.
Добрый день! 12. Инструкция по внедрению Расширения конфигурации (ОбменДаннымиОнлайнЧерезВебСервис.cfe)
(в процессе, будет чуть позже) есть где то? Не хочется снимать конфигурацию с поддержки.
Спасибо, пригодилось! Все отлично работает.
Все работает, большое спасибо!!!
статья отличная, только пришлось хорошо допилить план обмена, чтобы взлетело на УФ. Плюс еще ошибка была с реквизитом «ЭтотУзел», не знаю в какой версии его убрали, но у меня на платформе в режиме совместимости 8.3.10 и 8.2.13 такая ошибка есть. Делал обмен ДО 2.1->УПП 1.3.115
Если возникнет такая ошибка, можно воткнуть перед использованием реквизита «ЭтотУзел» такой код:
(25)
— Спасибо, буду иметь ввиду!
Сергей, спасибо за публикацию! Где можно увидеть 12. Инструкция по внедрению Расширения конфигурации (ОбменДаннымиОнлайнЧерезВебСервис.cfe)?
Напишите в личку, пожалуйста.
(27)
Инструкцию по внедрению расширения не писал. Здесь никаких особенностей нет. Нужно добавить в список расширений и прямо в коде исправить настройки на свои.
Сергей, а можно ли с вами связаться через альтернативные способы связи? Ответьте пожалуйста в личку, спасибо!
А для РИБа подойдет?
Эта доработка наврятли.
У меня есть доработка специально написанная для РИБ.
В которой стандартная выгрузка РИБ отправляется через Web.
Постараюсь в ближайшее время выложить.
Регистры сведений вот только не выгружаются… А как в правила подпихнуть отбор по регистру?
ИмяОтбора = СтрЗаменить(СтрПравил.ИмяОбъектаДляЗапроса, «.», «_»);
ПостроительОтчета = Новый ПостроительОтчета(«ВЫБРАТЬ ПЕРВЫЕ 1 _.* ИЗ » + СтрПравил.ИмяОбъектаДляЗапроса + » КАК _ {ГДЕ _.Ссылка.* КАК » + ИмяОтбора + «}»);
Вот вместо этого собственно.