КА -комплексная автоматизация, ДО - документооборот.
Итак, базы развернуты, опубликованы, можем начинать. А начнем с синхронизации...
Этап 1: Синхронизация
Настраиваем типовую синхронизацию, далее первый запуск и...ничего не работает. Идем в журнал регистрации и видим странную ошибку.
Неверное имя колонки {Обработка.КонвертацияОбъектовИнформационныхБаз.МодульОбъекта(8431)}: Коллекция.Колонки.Добавить(ИмяПоля); {Обработка.КонвертацияОбъектовИнформационныхБаз.МодульОбъекта(8310)}: КоллекцияГруппировки = ИнициализацияТаблицыПоКлючевымПолям(МассивКлючевыхПолейПоиска); {Обработка.КонвертацияОбъектовИнформационныхБаз.МодульОбъекта(9955)}: ЗагрузитьТабличнуюЧасть(Объект, Имя, ИнформацияОТипах, ПараметрыОбъекта, Правило); {Обработка.КонвертацияОбъектовИнформационныхБаз.МодульОбъекта(16050)}: ПоследнийОбъектЗагрузки = ПрочитатьОбъект(); {Обработка.КонвертацияОбъектовИнформационныхБаз.МодульОбъекта(2663)}: ПроизвестиЧтениеДанныхВРежимеВнешнегоСоединения(ЧтениеСообщения); {ОбщийМодуль.ОбменДаннымиСервер.Модуль(5794)}: ОбработкаОбменаДаннымиВнешнееСоединение.ВыполнитьВыгрузкуДанных(ОбработкаДляЗагрузкиДанных); {ОбщийМодуль.ОбменДаннымиСервер.Модуль(2936)}: ВыполнитьДействиеОбменаДляУзлаИнформационнойБазыПоВнешнемуСоединению(Отказ, {Обработка.ВыполнениеОбменаДанными.МодульМенеджера(34)}: ОбменДаннымиСервер.ВыполнитьОбменДаннымиДляУзлаИнформационнойБазы( {(1)}:Обработки.ВыполнениеОбменаДанными.ВыполнитьЗапускОбменаДанными(Параметры[0],Параметры[1]) {ОбщийМодуль.ОбщегоНазначения.Модуль(4884)}: Выполнить ИмяМетода + "(" + ПараметрыСтрока + ")"; {ОбщийМодуль.ДлительныеОперации.Модуль(724)}: ОбщегоНазначения.ВыполнитьМетодКонфигурации(ИмяПроцедуры, ПараметрыПроцедуры); {ОбщийМодуль.ДлительныеОперации.Модуль(715)}: ВыполнитьПроцедуру(ВсеПараметры.ИмяПроцедуры, ВсеПараметры.ПараметрыПроцедуры);
Что ж, делать нечего...Идем в отладку. И что мы там наблюдаем? Довольно интересную картину.
По какой-то причине у нас две колонки с одним наименованием.
Идем в правила конвертации и видим, что в тч контактной информации две строки с конвертацией реквизита ДействуетС, удаляем лишнюю строку, заменяем правила - и о чудо, все работает.
Больше с синхронизацией проблем не было (только доработки, связанные со спецификой внедрения). Переходим к бесшовной интеграции.
з.ы. ошибка эта была в нескольких релизах подряд, на последнем релизе не проверял, но при необходимости могу посмотреть.
Этап 2.Интеграция (бесшовка).
Историю о настройке бесшовной интеграции начну с небольшого описания работы самого механизма.
На стороне КА имеются следующие объекты:
ПланОбмена : ИнтеграцияС1СДокументооборотомПереопределяемый
РегистрСведений: ОчередьСообщенийВ1СДокументооборот
При изменении объекта, участвующего в интеграции, этот объект регистрируется на узле плана обмена. Далее фоновое задание вызывает процедуру модуля, в котором соответственно выполняются последовательно три процедуры:
ИнтеграцияС1СДокументооборотОбмен.ПодготовитьДанныеДляОтправки();
ИнтеграцияС1СДокументооборотОбмен.ОтправитьДанные();
ИнтеграцияС1СДокументооборотОбмен.ПолучитьДанные();
ПодготовитьДанныеДляОтправки(); - формирует пакеты в формате xml и помещает их в регистр сведений ОчередьСообщенийВ1СДокументооборот в виде двоичных данных. При этом пакеты формируются с контролем длины пакета, при превышении длины пакет разбивается на части. В одном пакете может содержаться несколько объектов, а значит если возникнет проблема загрузки хотя бы одного объекта из пакета - пакет не прогрузится. По факту это сделано в угоду производительности, но при этом сказалось на устойчивости системы. (В итоге после согласования с заказчиком была механизм был изменен под логику ОдинОбъект-ОдинПакет см. далее)
ОтправитьДанные() - получает записи регистра, получает из двоичных данных пакет, формирует на основе пакетов запрос, выполняет этот запрос на стороне ДО. В случае успешного выполнения запись в регистре удаляется.
Минус этого метода - все пакеты обрабатываются в одной конструкции Попытка-Исключение. Если хотя бы один пакет с ошибкой, все остальные пакеты уже не обрабатываются.
Также была переделана - каждый пакет обрабатывается в отдельной попытке.
ПолучитьДанные() - в цикле с уловием, пока есть необработанные пакеты на стороне ДО формируется запрос, который передает на сторону ДО номер последнего успешно обработанного пакета и возвращает в КА новый пакет, после чего данный пакет обрабатывается на стороне ДО. В типовом варианте также при возникновении ошибки загрузка останавливается. (Также была доработана).
На стороне ДО:
План обмена ИнтегрированныеСистемы
регистр сведений ОчередиСообщенийОбменаСИнтегрированнымиСистемами
справочник: СообщенияИнтегрированныхСистем
Фоновое задание формирует пакет xml и в виде двоичных данных записывает его в справочник.
Далее на стороне КА вызывается процедура ПолучитьДанные()
при успешном получении элемент справочника помечается на удаление.
2.1 Выгрузка из ДО в КА (не те правила подставляются)
ИнтеграцияС1СДокументооборотОбмен ПолучитьДанные();
В этой процедуре для объекта производится поиск подходящего правила. Ниже представлен скриншот функции, получающей правило интеграции для объекта.
Как видно из текста запроса, выбирается первое попавшееся правило, у которого тип исходного и конечного объекта совпадают с условием. В нашем случае для договоров было 3 правила, отличающихся заполнением реквизитов и видом внутреннего документа. В итоге при выгрузке выбиралось некорректное правило и дальше весь обмен стопорился с ошибками (набор заполняемых полей во всех 3 правилах был разным). Указано, что функция эта устарела, но при этом даже в самых последних релизах до сих пор используется.
Заменяем на функцию, указанную в рекомендациях (заодно делаем для себя дополнительную запись в Журнал регистрации.
Теперь правило определяются корректно и большая часть ошибок уже решена. Едем дальше...
2.2 Обмен заявками на расходование ДС по физ лицам
После создания правила для Заявки на расходование ДС - Внутренний документ (Заявка на расходование ДС) с частью заявок появились проблемы. А именно, у заявки вид хоз. операции "Выдача подотчетнику" банковский счет с владельцем физ. лицо. В правиле для банковского счета стоит флаг Обновлять. В ДО у банковского счета во владельцах справочника Физ. лица нет в принципе. При обмене из-за этого возникает ошибка "ОбщийМодуль.ИнтеграцияС1СДокументооборот.Модуль(467) Значение не является значением объектного типа (name)". - по отладке это как раз момент заполнения владельца у банковского счета. Сейчас пока выключили обновление у банковского счета, но это решение временное. параллельно написали в ТП 1с.
Таких заявок оказалось крайне мало, но по мне, это недоработка документооборота.
2.3 Обмен статусами документов
В КА помимо статуса в объекте есть регистр сведений СостоянияСогласованияВДокументообороте. В процедуре получения данных из ДО в этот регистр записываются данные по статусу, если они есть в пакете. После записи в регистр вызывается процедура ИнтеграцияС1СДокументооборотПереопределяемый.ПриИзмененииСостоянияСогласования в которой на текущий момент ничего нет (процедура пустая). Я предполагаю, что данный регистр в дальнейшем нужно обрабатывать, получая статус и объект по идентификатору и далее изменять статус в самом объекте. Но мы идем немного другим путем и прописываем соответствия статусов непосредственно в правилах интеграции.
Перечь статусов в КА и ДО не соответствует, поэтому их нужно синхронизировать с помощью выражений.
Из КА в ДО:
Если Параметры.Источник.Статус = Перечисления.СтатусыЗаявокНаРасходованиеДенежныхСредств.НеСогласована Тогда
Параметры.Результат = "НеСогласован";
ИначеЕсли Параметры.Источник.Статус = Перечисления.СтатусыЗаявокНаРасходованиеДенежныхСредств.Согласована Тогда
Параметры.Результат = "Согласован";
ИначеЕсли Параметры.Источник.Статус = Перечисления.СтатусыЗаявокНаРасходованиеДенежныхСредств.КОплате Тогда
Параметры.Результат = "Согласован";
ИначеЕсли Параметры.Источник.Статус = Перечисления.СтатусыЗаявокНаРасходованиеДенежныхСредств.Отклонена Тогда
Параметры.Результат = "НеСогласован";
КонецЕсли;
ЗаписьЖурналаРегистрации("интеграция Отправка",УровеньЖурналаРегистрации.Информация,,Параметры.Результат,,);
Из ДО в КА:
пСтатус = Неопределено;
Если Параметры.Источник.statusApproval.name = "Согласован" Тогда
пСтатус = Перечисления.СтатусыЗаявокНаРасходованиеДенежныхСредств.Согласована;
ИначеЕсли Параметры.Источник.statusApproval.name = "Не согласован" Тогда
пСтатус = Перечисления.СтатусыЗаявокНаРасходованиеДенежныхСредств.НеСогласована;
ИначеЕсли Параметры.Источник.statusApproval.name = "На согласовании" Тогда
пСтатус = Перечисления.СтатусыЗаявокНаРасходованиеДенежныхСредств.НеСогласована;
КонецЕсли;
пСтатусДляЖР = "статус " + Параметры.Источник.status.name+"; объект "+Параметры.Источник.title+"; ст согл "+Параметры.Источник.statusApproval.name;
ЗаписьЖурналаРегистрации("интеграция Получение",УровеньЖурналаРегистрации.Информация,,пСтатусДляЖР,,);
Параметры.Результат = пСтатус;
Также столкнулись с такой проблемой: в КА заявку перевели в статус "К оплате", через некоторое время из ДО прилетает статус "Согласовано". Пришлось на стороне КА делать проверку, если статус "К оплате", то обмен его уже изменить не может. Такие заявки контролируются ответственным пользователем.
3. Меняем логику работы обмена.
После обсуждений с заказчиком было принято решение о том, что текущую логику работы обмена нужно поменять. А именно:
-сделать проверку на количество итераций по ошибочным пакетам при загрузке из ДО в КА;
-если ошибочный пакет грузится повторно, присваиваем ему статус обработанного с занесением ошибки в ЖР (в дальнейшем планируется отдельный регистр + рассылка ошибок по ответственным);
-под каждый объект делаем свой пакет, т.е если в очереди будет ошибочный объект, то он не повлияет на остальные пакеты в очереди;
В итоге доработки выглядят так:
&Вместо("ПолучитьДанные")
Процедура is4b_ПолучитьДанные()
Попытка
Прокси = ИнтеграцияС1СДокументооборотПовтИсп.ПолучитьПрокси();
ПоддерживаетсяОбновлениеФайлов =
ИнтеграцияС1СДокументооборотПовтИсп.ДоступенФункционалВерсииСервиса("2.1.9.1.CORP");
ОбъектыКОбновлениюПечатныхФорм = Новый ТаблицаЗначений;
ОбъектыКОбновлениюПечатныхФорм.Колонки.Добавить("Объект");
ОбъектыКОбновлениюПечатныхФорм.Колонки.Добавить("ОбъектXDTO");
ОбъектыКОбновлениюПечатныхФорм.Колонки.Добавить("Правило");
УзелДокументооборота = ИнтеграцияС1СДокументооборотПовтИсп.УзелДокументооборота();
СоставПланаОбмена = Метаданные.ПланыОбмена.ИнтеграцияС1СДокументооборотомПереопределяемый.Состав;
ПрочитаныВсеСообщения = Ложь;
МассивОшибочныхПакетов = Новый Массив;
СчетчикИтераций = 0;
Пока Не ПрочитаныВсеСообщения
И МассивОшибочныхПакетов.Количество()<10
И СчетчикИтераций<50 Цикл
СчетчикИтераций = СчетчикИтераций+1;
//Каждое сообщение обрабатываем в отдельной попытке
Попытка
Запрос = ИнтеграцияС1СДокументооборот.СоздатьОбъект(Прокси, "DMGetChangesRequest");
Запрос.lastMessageId = Константы.НомерПоследнегоПринятогоСообщенияДокументооборота.Получить();
Ответ = Прокси.execute(Запрос);
ИнтеграцияС1СДокументооборот.ПроверитьВозвратВебСервиса(Прокси, Ответ);
//bmv
//проверяем, если уже была попытка записи и завершилась с ошибокой
//то помечаем этот пакет, как обработанный в ДО
ИндексПакетаВМассиве =МассивОшибочныхПакетов.Найти(Ответ.messageId);
Если ИндексПакетаВМассиве<>Неопределено Тогда
МассивОшибочныхПакетов.Удалить(ИндексПакетаВМассиве);
НомерИсходный = Константы.НомерПоследнегоПринятогоСообщенияДокументооборота.Получить();
НомерПринятый = Строка(Ответ.messageId);
Если НомерИсходный <> НомерПринятый Тогда
Константы.НомерПоследнегоПринятогоСообщенияДокументооборота.Установить(НомерПринятый);
КонецЕсли;
Продолжить;
КонецЕсли;
//bmv
Для каждого ОбъектXDTO Из Ответ.objects Цикл
Если ОбъектXDTO.objectId.type = "DMInternalDocumentTemplate"
Или ОбъектXDTO.objectId.type = "DMIncomingDocumentTemplate"
Или ОбъектXDTO.objectId.type = "DMOutgoingDocumentTemplate" Тогда
Справочники.ПравилаИнтеграцииС1СДокументооборотом.ОбновитьПравилаПоШаблону(ОбъектXDTO);
Продолжить;
КонецЕсли;
Ссылки = ИнтеграцияС1СДокументооборот.СсылкиПоВнешнимОбъектам(ОбъектXDTO);
Для Каждого ОбъектСсылка Из Ссылки Цикл
//Правило = Справочники.ПравилаИнтеграцииС1СДокументооборотом.ПравилоИнтеграцииОбъекта(
// ОбъектСсылка, ОбъектXDTO.objectId.type);
//bmv 26_10_20
Правило = Неопределено;
Правила = ИнтеграцияС1СДокументооборотВызовСервера.ПодходящиеПравила(ОбъектСсылка,ОбъектXDTO);
Если Правила.Количество()=1 Тогда
Правило = Правила[0].Ссылка;
КонецЕсли;
ЗаписьЖурналаРегистрации("ПолучениеИзДО.ПодборПравилИнтеграции", УровеньЖурналаРегистрации.Информация,,ОбъектСсылка,"Выбрано правило "+ Правило);
//bmv 26_10_20
Если Правило = Неопределено Тогда
Продолжить;
КонецЕсли;
Если ОбъектXDTO.Свойства().Получить("files") <> Неопределено
И ОбъектXDTO.Установлено("files") Тогда
Если ОбъектXDTO.files.Количество() <> 0 Тогда
ИнтеграцияС1СДокументооборотПереопределяемый.ПриПоявленииПрисоединенныхФайловДокументооборота(
ОбъектXDTO.objectId.id,
ОбъектXDTO.objectId.type,
ОбъектСсылка);
Иначе
ИнтеграцияС1СДокументооборотПереопределяемый.ПриУдаленииПрисоединенныхФайловДокументооборота(
ОбъектXDTO.objectId.id,
ОбъектXDTO.objectId.type,
ОбъектСсылка);
КонецЕсли;
КонецЕсли;
Объект = ОбъектСсылка.ПолучитьОбъект();
ИсходнаяПометкаУдаления = Объект.ПометкаУдаления;
Обновление = Истина;
ТребуетсяПерепроведение = Ложь;
ЕстьИзменения = Справочники.ПравилаИнтеграцииС1СДокументооборотом.ЗаполнитьОбъектПоОбъектуXDTO(
Прокси,
Объект,
ОбъектXDTO,
Правило,
Обновление,
ТребуетсяПерепроведение);
Если ЕстьИзменения Тогда
Если Объект.ПометкаУдаления
И Не ИсходнаяПометкаУдаления Тогда
МетаданныеОбъекта = Объект.Метаданные();
Если Метаданные.Документы.Содержит(МетаданныеОбъекта)
И МетаданныеОбъекта.Проведение = Метаданные.СвойстваОбъектов.Проведение.Разрешить
И Объект.Проведен Тогда
Объект.Записать(РежимЗаписиДокумента.ОтменаПроведения);
Иначе
Объект.ОбменДанными.Загрузка = Истина;
Объект.Записать();
КонецЕсли;
Иначе
ЗаполненКорректно = Объект.ПроверитьЗаполнение();
Если ЗаполненКорректно Тогда
Если ТребуетсяПерепроведение Тогда
Объект.Записать(РежимЗаписиДокумента.Проведение,
РежимПроведенияДокумента.Неоперативный);
Иначе
Объект.ОбменДанными.Загрузка = Истина;
Объект.Записать();
КонецЕсли;
Иначе
СообщенияПользователю = ПолучитьСообщенияПользователю(Истина);
ТекстСообщения = "";
Для Каждого СообщениеПользователю Из СообщенияПользователю Цикл
ТекстСообщения = ТекстСообщения + СообщениеПользователю.Текст + Символы.ПС;
КонецЦикла;
ВызватьИсключение ТекстСообщения;
КонецЕсли;
КонецЕсли;
Если ПоддерживаетсяОбновлениеФайлов
И ИнтеграцияС1СДокументооборотКлиентСервер.ЭтоДокумент(ОбъектXDTO.objectId.type) Тогда
КОбновлению = ОбъектыКОбновлениюПечатныхФорм.Добавить();
КОбновлению.Объект = Объект;
КОбновлению.ОбъектXDTO = ОбъектXDTO;
КОбновлению.Правило = Правило;
КонецЕсли;
Если СоставПланаОбмена.Содержит(Объект.Метаданные()) Тогда
ПланыОбмена.УдалитьРегистрациюИзменений(УзелДокументооборота, Объект.Ссылка);
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЦикла;
// С версии 1.4.8 выполняется обновление состояний согласования на стороне ИС.
Если Ответ.Свойства().Получить("records") <> Неопределено Тогда
Для каждого ОбъектXDTO Из Ответ.records Цикл
ЗапросСвязанныйОбъект = Новый Запрос(
"ВЫБРАТЬ ПЕРВЫЕ 1
| Объект
|ИЗ
| РегистрСведений.ОбъектыИнтегрированныеС1СДокументооборотом
|ГДЕ
| ТипОбъектаДО = &ТипОбъектаДО
| И ИдентификаторОбъектаДО = &ИдентификаторОбъектаДО
|");
Если ИнтеграцияС1СДокументооборот.ПроверитьТип(Прокси, ОбъектXDTO,
"DMApprovalStateRecord") Тогда
ЗапросСвязанныйОбъект.УстановитьПараметр("ТипОбъектаДО",
ОбъектXDTO.type);
ЗапросСвязанныйОбъект.УстановитьПараметр("ИдентификаторОбъектаДО",
ОбъектXDTO.id);
Выборка = ЗапросСвязанныйОбъект.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Если ОбъектXDTO.status = Неопределено Тогда // запись удалена (например, прерывание)
ИнтеграцияС1СДокументооборотВызовСервера.ПриИзмененииСостоянияСогласования(
ОбъектXDTO.id,
ОбъектXDTO.type,
Неопределено,
Ложь,
Выборка.Объект);
Иначе
ИдентификаторСостояния = ОбъектXDTO.status.objectId.id;
Состояние = Перечисления.СостоянияСогласованияВДокументообороте[ИдентификаторСостояния];
ИнтеграцияС1СДокументооборотВызовСервера.ПриИзмененииСостоянияСогласования(
ОбъектXDTO.id,
ОбъектXDTO.type,
Состояние,
Ложь,
Выборка.Объект,
ОбъектXDTO.name,
ОбъектXDTO.date);
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЕсли;
НомерИсходный = Константы.НомерПоследнегоПринятогоСообщенияДокументооборота.Получить();
НомерПринятый = Строка(Ответ.messageId);
Если НомерИсходный <> НомерПринятый Тогда
Константы.НомерПоследнегоПринятогоСообщенияДокументооборота.Установить(НомерПринятый);
КонецЕсли;
ПрочитаныВсеСообщения = (Ответ.messageId = Неопределено);
ОбновитьПечатныеФормы(ОбъектыКОбновлениюПечатныхФорм);
Исключение
МассивОшибочныхПакетов.Добавить(Ответ.messageId);
Инфо = ОписаниеОшибки();
ЗаписьЖурналаРегистрации(
ИнтеграцияС1СДокументооборот.ИмяСобытияЖурналаРегистрации(
НСтр("ru = 'Получение данных'",
ОбщегоНазначенияКлиентСервер.КодОсновногоЯзыка())),
УровеньЖурналаРегистрации.Ошибка,
,
Запрос.Тип().Имя,
Запрос.Тип().Имя + Символы.ПС + Инфо+"; ИД пакета: "+Ответ.messageId);
КонецПопытки;
КонецЦикла;
Исключение
Инфо = ОписаниеОшибки();
ЗаписьЖурналаРегистрации(
ИнтеграцияС1СДокументооборот.ИмяСобытияЖурналаРегистрации(
НСтр("ru = 'Получение данных'",
ОбщегоНазначенияКлиентСервер.КодОсновногоЯзыка())),
УровеньЖурналаРегистрации.Ошибка,
,
Запрос.Тип().Имя,
Запрос.Тип().Имя + Символы.ПС + Инфо);
КонецПопытки;
КонецПроцедуры
&Вместо("ПодготовитьДанныеДляОтправки")
Процедура is4b_ПодготовитьДанныеДляОтправки()
УзелДокументооборота = ИнтеграцияС1СДокументооборотПовтИсп.УзелДокументооборота();
Прокси = ИнтеграцияС1СДокументооборотПовтИсп.ПолучитьПрокси();
// Выборка всех изменений для данной интегрированной системы
ИнтегрированныеОбъекты = ПолучитьМассивЗарегистрированныхДанных(УзелДокументооборота);
Если ИнтегрированныеОбъекты.Количество() = 0 Тогда
Возврат;
КонецЕсли;
//bmv 30_10_20
МассивУспешноОбработаны = Новый Массив;
//bmv 30_10_20
Для Каждого ИнтегрированныйОбъект Из ИнтегрированныеОбъекты Цикл
//bmv 30_10_20
//под каждый объект свой пакет
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xml");
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяВременногоФайла, "UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("Message");
//bmv 30_10_20
ОбъектXDTO = ИнтеграцияС1СДокументооборот.ПолучитьXDTOИзмененийИзОбъекта(Прокси, ИнтегрированныйОбъект);
Если ОбъектXDTO = Неопределено Тогда
Продолжить;
КонецЕсли;
Попытка
Прокси.ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, ОбъектXDTO);
//bmv 30_10_20
МассивУспешноОбработаны.Добавить(ИнтегрированныйОбъект);
//bmv 30_10_20
Исключение
Инфо = ОписаниеОшибки();
ЗаписьЖурналаРегистрации(
ИнтеграцияС1СДокументооборот.ИмяСобытияЖурналаРегистрации(НСтр("ru = 'Выгрузка XDTO в XML'",
ОбщегоНазначенияКлиентСервер.КодОсновногоЯзыка())),
УровеньЖурналаРегистрации.Ошибка,
,
ОбъектXDTO.Тип().Имя,
ОбъектXDTO.Тип().Имя + Символы.ПС + Инфо);
КонецПопытки;
//bmv 30_10_20
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.Закрыть();
ДвоичныеДанныеСообщения = Новый ДвоичныеДанные(ИмяВременногоФайла);
ДанныеСообщения = Новый ХранилищеЗначения(ДвоичныеДанныеСообщения, Новый СжатиеДанных(9));
РегистрыСведений.ОчередьСообщенийВ1СДокументооборот.ДобавитьСообщение(ДанныеСообщения);
#Если Сервер Тогда
УдалитьФайлы(ИмяВременногоФайла);
#КонецЕсли
//bmv 30_10_20
КонецЦикла;
//bmv 30_10_20
//Для Каждого ИнтегрированныйОбъект Из ИнтегрированныеОбъекты Цикл
Если МассивУспешноОбработаны.Количество()>0 Тогда
Для Каждого ИнтегрированныйОбъект Из МассивУспешноОбработаны Цикл
ПланыОбмена.УдалитьРегистрациюИзменений(УзелДокументооборота, ИнтегрированныйОбъект.Объект);
КонецЦикла;
КонецЕсли;
//bmv 30_10_20
КонецПроцедуры
&Вместо("ОтправитьДанные")
Процедура is4b_ОтправитьДанные()
Попытка
ИдентификаторСообщения = Неопределено;
МоментВремени = Неопределено;
Запрос = Новый Запрос(
"ВЫБРАТЬ
| ОчередьСообщенийВ1СДокументооборот.МоментВремени КАК МоментВремени,
| ОчередьСообщенийВ1СДокументооборот.Данные КАК Данные,
| ОчередьСообщенийВ1СДокументооборот.Идентификатор КАК Идентификатор
|ИЗ
| РегистрСведений.ОчередьСообщенийВ1СДокументооборот КАК ОчередьСообщенийВ1СДокументооборот
|
|УПОРЯДОЧИТЬ ПО
| МоментВремени");
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
Возврат;
КонецЕсли;
Прокси = ИнтеграцияС1СДокументооборотПовтИсп.ПолучитьПрокси();
//Запрос = ИнтеграцияС1СДокументооборот.СоздатьОбъект(Прокси, "DMPutChangesRequest");
//ОбщийРазмерСообщений = 0;
ПредельныйРазмерСообщений =
ИнтеграцияС1СДокументооборотВызовСервера.МаксимальныйРазмерПередаваемогоФайла();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
//bmv 30_10_20
Попытка
Запрос = ИнтеграцияС1СДокументооборот.СоздатьОбъект(Прокси, "DMPutChangesRequest");
ИдентификаторСообщения = Выборка.Идентификатор;
МоментВремени = Выборка.МоментВремени;
ДвоичныеДанные = Выборка.Данные.Получить();
РазмерДвоичныеДанные = ДвоичныеДанные.Размер();
//ОбщийРазмерСообщений = ОбщийРазмерСообщений + ДвоичныеДанные.Размер();
//Если ОбщийРазмерСообщений > ПредельныйРазмерСообщений
// И Запрос.objects.Количество() > 0 Тогда
Если РазмерДвоичныеДанные>ПредельныйРазмерСообщений Тогда
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Интеграция с 1С:Документооборотом.Отправка данных'",
ОбщегоНазначенияКлиентСервер.КодОсновногоЯзыка()),
УровеньЖурналаРегистрации.Ошибка,
Метаданные.РегистрыСведений.ОчередьСообщенийВ1СДокументооборот,
Строка(ИдентификаторСообщения),
" превышен максимальный размер пакета");
Продолжить;
КонецЕсли;
//Результат = Прокси.execute(Запрос);
//ИнтеграцияС1СДокументооборот.ПроверитьВозвратВебСервиса(Прокси, Результат);
//Запрос = ИнтеграцияС1СДокументооборот.СоздатьОбъект(Прокси, "DMPutChangesRequest");
//ОбщийРазмерСообщений = 0;
ИмяФайлаСообщенияОбмена = ПолучитьИмяВременногоФайла("xml");
ДвоичныеДанные.Записать(ИмяФайлаСообщенияОбмена);
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайлаСообщенияОбмена);
ЧтениеXML.Прочитать();
ЧтениеXML.Прочитать();
Пока ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Цикл
// Выполняется последовательное чтение одного объекта за другим
ТипXDTO = Прокси.ФабрикаXDTO.Тип("http://www.1c.ru/dm", ЧтениеXML.Имя);
ОбъектXDTO = Прокси.ФабрикаXDTO.ПрочитатьXML(ЧтениеXML, ТипXDTO);
Запрос.objects.Добавить(ОбъектXDTO);
КонецЦикла;
ЧтениеXML = Неопределено;
#Если Сервер Тогда
УдалитьФайлы(ИмяФайлаСообщенияОбмена);
#КонецЕсли
Результат = Прокси.execute(Запрос);
ИнтеграцияС1СДокументооборот.ПроверитьВозвратВебСервиса(Прокси, Результат);
//если ошибок нет, сразу удаляем запись в регистре
МенеджерЗаписи = РегистрыСведений.ОчередьСообщенийВ1СДокументооборот.СоздатьМенеджерЗаписи();
ЗаполнитьЗначенияСвойств(МенеджерЗаписи, Выборка);
МенеджерЗаписи.Удалить();
Исключение
Инфо = ОписаниеОшибки();
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Интеграция с 1С:Документооборотом.Отправка данных'",
ОбщегоНазначенияКлиентСервер.КодОсновногоЯзыка()),
УровеньЖурналаРегистрации.Ошибка,
Метаданные.РегистрыСведений.ОчередьСообщенийВ1СДокументооборот,
Строка(ИдентификаторСообщения),
Запрос.Тип().Имя + Символы.ПС + Инфо);
ОбновитьДанныеПоОшибке(ИдентификаторСообщения, МоментВремени, Инфо);
КонецПопытки;
КонецЦикла;
//Если Запрос.objects.Количество() > 0 Тогда
// Результат = Прокси.execute(Запрос);
// ИнтеграцияС1СДокументооборот.ПроверитьВозвратВебСервиса(Прокси, Результат);
//КонецЕсли;
//Выборка.Сбросить();
//Пока Выборка.Следующий() Цикл
//КонецЦикла;
Исключение
Инфо = ОписаниеОшибки();
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Интеграция с 1С:Документооборотом.Отправка данных'",
ОбщегоНазначенияКлиентСервер.КодОсновногоЯзыка()),
УровеньЖурналаРегистрации.Ошибка,
Метаданные.РегистрыСведений.ОчередьСообщенийВ1СДокументооборот,
Строка(ИдентификаторСообщения),
Запрос.Тип().Имя + Символы.ПС + Инфо);
ОбновитьДанныеПоОшибке(ИдентификаторСообщения, МоментВремени, Инфо);
КонецПопытки;
КонецПроцедуры
Процедура ОбновитьДанныеПоОшибке(ИдентификаторСообщения, МоментВремени, Инфо)
Если ИдентификаторСообщения <> Неопределено Тогда
МенеджерЗаписи = РегистрыСведений.ОчередьСообщенийВ1СДокументооборот.СоздатьМенеджерЗаписи();
МенеджерЗаписи.МоментВремени = МоментВремени;
МенеджерЗаписи.Идентификатор = ИдентификаторСообщения;
МенеджерЗаписи.Прочитать();
МенеджерЗаписи.КоличествоПопытокОтправки = МенеджерЗаписи.КоличествоПопытокОтправки + 1;
МенеджерЗаписи.ТекстСообщенияОбОшибке = Инфо;
МенеджерЗаписи.Записать();
КонецЕсли;
КонецПроцедуры
&Вместо("ФормированиеСообщений")
Процедура is4b_ФормированиеСообщений()
ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания();
УзлыОбмена = ОбработкаЗапросовXDTOПовтИсп.ПолучитьУзлыОбменаИнтегрированныхСистем();
Если Не ПолучитьФункциональнуюОпцию("ИспользоватьСинхронизациюСИнтегрированнымиСистемами") Тогда
Для каждого ИнтегрированнаяСистема Из УзлыОбмена Цикл
ПланыОбмена.УдалитьРегистрациюИзменений(ИнтегрированнаяСистема);
КонецЦикла;
Возврат;
КонецЕсли;
Для каждого ИнтегрированнаяСистема Из УзлыОбмена Цикл
СформироватьПакетыОбмена(ИнтегрированнаяСистема);
//Сообщение = СоздатьНовоеСообщение(ИнтегрированнаяСистема);
//СформироватьПакетОбмена(Сообщение, ИнтегрированнаяСистема);
КонецЦикла;
КонецПроцедуры
Процедура СформироватьПакетыОбмена(Узел)
УстановитьПривилегированныйРежим(Истина);
ПараметрыСеанса.УзелИнтегрированнойСистемы = Узел; // для кода, формирующего XDTO-объекты
МассивНеобработанныхОбъектов = Новый Массив;
//Сначала получаем все данные с момента последнего обмена
НомерСообщения = 1;
ПланыОбмена.ВыбратьИзменения(Узел, НомерСообщения);
// Выборка всех изменений
ДанныеДляПередачи = ПолучитьДанныеДляПередачи(Узел, НомерСообщения);
//КоличествоОбъектовВсего = ДанныеДляПередачи.Количество();
//ВВыборкеЕстьДанные = КоличествоОбъектовВсего > 0;
НачатьТранзакцию();
//В цикле по выбранным объектам формируем сообщения и заполняем данными
Для Каждого ЭлементДанных Из ДанныеДляПередачи Цикл
ОбъектыXDTO = Новый Массив;
ЗаписиXDTO = Новый Массив;
Сообщение = СоздатьНовоеСообщение(Узел);
НачалоПодготовки = ТекущаяУниверсальнаяДатаВМиллисекундах();
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xml");
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяВременногоФайла, "UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();
Попытка
ПолучитьXDTOИзОбъекта(ЭлементДанных, ОбъектыXDTO, ЗаписиXDTO);
//ВызватьИсключение("тест"); //тестируем ошибочную ситуацию
Исключение
Инфо = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Обмен с интегрированными системами.Формирование сообщения'", Метаданные.ОсновнойЯзык.КодЯзыка),
УровеньЖурналаРегистрации.Ошибка,,
ЭлементДанных,
ПодробноеПредставлениеОшибки(Инфо));
//ВызватьИсключение;
//Если формирование объекта прошло с ошибкой, тогда удаляем сообщение для этого пакета, очищаем записьХМЛ
СообщениеОбъект = Сообщение.ПолучитьОбъект();
СообщениеОбъект.Удалить();
ЗаписьXML.Закрыть();
УдалитьФайлы(ИмяВременногоФайла);
МассивНеобработанныхОбъектов.Добавить(ЭлементДанных);
Продолжить;
КонецПопытки;
// Расчет процента готовности сообщения.
// Максимальное значение 99, т.к. необходимо гарантировать,
// что не будет выполняться попытка передать сообщение клиенту до того,
// как данные сообщения будут записаны.
СчетчикОбъектов = 1;
ПроцентГотовности = Окр(99 * СчетчикОбъектов);
РегистрыСведений.СтепеньГотовностиСообщенийИнтегрированныхСистем.УстановитьПроцентГотовности(
Сообщение, ПроцентГотовности);
КонецПодготовки = ТекущаяУниверсальнаяДатаВМиллисекундах();
РегистрыСведений.СведенияОСообщенияхОбменаСИнтегрированнымиСистемами.ЗаписатьВремяПодготовки(
Сообщение,
(КонецПодготовки - НачалоПодготовки)/1000);
ВыгрузитьМассивXDTOВСообщение(ИмяВременногоФайла, ЗаписьXML, ОбъектыXDTO, ЗаписиXDTO, Сообщение);
УдалитьФайлы(ИмяВременногоФайла);
КонецЦикла;
ПланыОбмена.УдалитьРегистрациюИзменений(Узел, НомерСообщения);
//Если были ошибочные данные, перерегистрируем их на узле
Если МассивНеобработанныхОбъектов.Количество()>0 Тогда
Для Каждого ОшибочныйОбъект Из МассивНеобработанныхОбъектов Цикл
ПланыОбмена.ЗарегистрироватьИзменения(Узел,ОшибочныйОбъект);
КонецЦикла;
КонецЕслИ;
ЗафиксироватьТранзакцию();
КонецПроцедуры
&Вместо("ПолучитьИзменения")
Функция is4b_ПолучитьИзменения(Сообщение)
Если ПолучитьФункциональнуюОпцию("ИспользоватьСинхронизациюСИнтегрированнымиСистемами") = Ложь Тогда
Константы.ИспользоватьСинхронизациюСИнтегрированнымиСистемами.Установить(Истина);
КонецЕсли;
Ответ = ОбработкаЗапросовXDTO.СоздатьОбъект("DMGetChangesResponse");
Узел = ОбработкаЗапросовXDTOКОРП.УзелИнтегрированнойСистемыПоСообщению(Сообщение);
ИдентификаторПринятогоСообщения = Сообщение.lastMessageId;
Если ИдентификаторПринятогоСообщения <> Неопределено Тогда
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| СообщенияИнтегрированныхСистем.Ссылка
|ИЗ
| Справочник.СообщенияИнтегрированныхСистем КАК СообщенияИнтегрированныхСистем
|ГДЕ
| СообщенияИнтегрированныхСистем.ПометкаУдаления = ЛОЖЬ
| И СообщенияИнтегрированныхСистем.ИдентификаторСообщения = &ИдентификаторСообщения
| И СообщенияИнтегрированныхСистем.Входящее = ЛОЖЬ";
Запрос.УстановитьПараметр("ИдентификаторСообщения", Строка(ИдентификаторПринятогоСообщения));
ВыборкаПринятыеСообщения = Запрос.Выполнить().Выбрать();
Если ВыборкаПринятыеСообщения.Следующий() Тогда
ПринятоеСообщение = ВыборкаПринятыеСообщения.Ссылка;
ПринятоеСообщениеОбъект = ПринятоеСообщение.ПолучитьОбъект();
ПринятоеСообщениеОбъект.УстановитьПометкуУдаления(Истина);
РегистрыСведений.СведенияОСообщенияхОбменаСИнтегрированнымиСистемами.ЗаписатьДатуПередачиКлиенту(
ПринятоеСообщение,
ТекущаяДатаСеанса());
ИдентификаторПринятогоСообщения = Неопределено;
Иначе
ИдентификаторПринятогоСообщения = Неопределено;
КонецЕсли;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| СообщенияИнтегрированныхСистем.Ссылка,
| СообщенияИнтегрированныхСистем.ДанныеСообщения КАК Данные,
| СообщенияИнтегрированныхСистем.ДатаСоздания КАК ДатаСоздания,
| СообщенияИнтегрированныхСистем.ИдентификаторСообщения
|ИЗ
| Справочник.СообщенияИнтегрированныхСистем КАК СообщенияИнтегрированныхСистем
|ВНУТРЕННЕЕ СОЕДИНЕНИЕ
| РегистрСведений.ОчередиСообщенийОбменаСИнтегрированнымиСистемами КАК ОчередиСообщенийОбменаСИнтегрированнымиСистемами
|ПО
| СообщенияИнтегрированныхСистем.Очередь = ОчередиСообщенийОбменаСИнтегрированнымиСистемами.Очередь
|ВНУТРЕННЕЕ СОЕДИНЕНИЕ
| РегистрСведений.СтепеньГотовностиСообщенийИнтегрированныхСистем КАК СтепеньГотовностиСообщенийИнтегрированныхСистем
|ПО
| СообщенияИнтегрированныхСистем.Ссылка = СтепеньГотовностиСообщенийИнтегрированныхСистем.Сообщение
|ГДЕ
| ОчередиСообщенийОбменаСИнтегрированнымиСистемами.ИнтегрированнаяСистема = &ИнтегрированнаяСистема
| И НЕ СообщенияИнтегрированныхСистем.ПометкаУдаления
| И СтепеньГотовностиСообщенийИнтегрированныхСистем.ПроцентГотовности = 100
|
|УПОРЯДОЧИТЬ ПО
| ДатаСоздания УБЫВ";
Запрос.УстановитьПараметр("ИнтегрированнаяСистема", Узел);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
ИмяФайлаСообщенияОбмена = ПолучитьИмяВременногоФайла("xml");
ДвоичныеДанные = Выборка.Данные.Получить();
ДвоичныеДанные.Записать(ИмяФайлаСообщенияОбмена);
СсылочныйТип = ФабрикаXDTO.Тип("http://www.1c.ru/dm", "DMObject");
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайлаСообщенияОбмена);
ЧтениеXML.Прочитать(); // корневой элемент "Message"
ЧтениеXML.Прочитать();
Пока ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Цикл
ТипXDTO = ФабрикаXDTO.Тип("http://www.1c.ru/dm", ЧтениеXML.Имя);
ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML, ТипXDTO);
Если СсылочныйТип.ЭтоПотомок(ТипXDTO) Тогда
Ответ.objects.Добавить(ОбъектXDTO);
Иначе // записи РС и тому подобные объекты не ссылочных типов
Ответ.records.Добавить(ОбъектXDTO);
КонецЕсли;
КонецЦикла;
Ответ.messageId = Строка(Выборка.ИдентификаторСообщения);
КонецЕсли;
Возврат Ответ;
КонецФункции
Итог: синхронизация и бесшовная интеграция по правилам была настроена и запущена. При возникновении ошибок обмен не останавливается, ошибки фиксируются. Да, не совсем правильно помечать ошибочные пакеты из очереди как обработанные, но в нашем случае по ошибкам будет настроено оповещение ответственных лиц и каждый случай будет проверяться в дальнейшем. Как показывает практика, даже если разворачивать "из коробки" типовые решения, то корректно они работают далеко не всегда.