К слову, в БСП 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" и посмотреть как это выполнено в них.