Проблема, собственно, в названии темы.
Имеется УТ 11.3 и ДО 2.1.
Настроен обмен УТ->ДО договорами.
С чего все началось... пользователи стали жаловаться, что невозможно внести изменения в справочник "Организации" в ДО - "Данные были изменены или удалены другим пользователем".
Смотрю ЖР. Оказывается, что WS-соединение почти каждые 3 секунды перезаписывает организацию!
В правилах интеграции ничего криминального нет, они почти те, что в типовой методичке приведены.
Далее смотрю регистр "Очередь сообщений в 1С:Документооборот".
Оказывается, там аж 2500 записей об ошибках.
Типичные из них:
{ОбщийМодуль.ИнтеграцияС1СДокументооборот.Модуль(390)}: Ошибка при записи внутреннего документа:¶Регистрационный номер не уникален!¶
{ОбщийМодуль.ИнтеграцияС1СДокументооборотОбмен.Модуль(346)}: Ошибка при вызове метода контекста (execute): При вызове веб-сервиса произошла ошибка. Ошибка вызова операции сервиса: {http://www.1c.ru/dm}:DMService:execute(): При вызове веб-сервиса произошла ошибка. Неизвестная ошибка. Ошибка работы с Интернет: Превышено время ожидания: Ошибка работы с Интернет: Превышено время ожидания
{ОбщийМодуль.ИнтеграцияС1СДокументооборот.Модуль(390)}: Ошибка при изменении внутреннего документа:¶Не удалось заблокировать запись. Действие (изменение, удаление или блокировка записи) не выполнено.¶Ошибка блокировки объекта. Объект уже заблокирован:¶пользователь: Администратор, сеанс: 294, начат: 03.09.2018 в 10:30:31, приложение: WS-соединение¶
Судя по регламентному заданию "Интеграция с 1С:Документооборотом - Выполнить обмен данными" то этот регистр должен чиститься после каждого успешного обмена. Что будет если просто его очистить? Изменения не перетекут в ДО?
А теперь самое интересное!
Залезаю в отладку (ДО) и вижу, такое (см. скриншот "Стек вызовов").
Оказывается, при любом раскладе в ОбработкаЗапросовXDTO.НайтиСоздатьОрганизацию() будет перезаписана организация! Будь она найдена по внутреннему id или ключам поиска (наименование, инн/кпп):
Полное описание функции
// Находит подходящую или создает новую организацию по объекту XDTO
//
// Параметры:
// Узел - ПланОбменаСсылка.ИнтегрированныеСистемы - узел интегрированной системы
// ОбъектXDTO - ОбъектXDTO типа DMOrganization
//
// Возвращаемое значение:
// СправочникСсылка.Организации - найденная или созданная организация
//
Функция НайтиСоздатьОрганизацию(Узел, ОбъектXDTO) Экспорт
ВнешнийID = ОбъектXDTO.externalObject.id;
ВнешнийТип = ОбъектXDTO.externalObject.type;
СсылкаДО = Неопределено;
// синхронизация по наименованию
Если Не ЗначениеЗаполнено(ВнешнийID) Тогда
Наименование = Неопределено;
ОбработкаЗапросовXDTO.ЗаполнитьРеквизитИзСвойстваXDTO(Узел, Наименование, ОбъектXDTO, "name");
СсылкаДО = Справочники.Организации.НайтиПоНаименованию(Наименование, Истина);
Если Не ЗначениеЗаполнено(СсылкаДО) Тогда
ОбъектДО = Справочники.Организации.СоздатьЭлемент();
ОбъектДО.Наименование = Наименование;
ОбъектДО.Записать();
СсылкаДО = ОбъектДО.Ссылка;
КонецЕсли;
Возврат СсылкаДО;
КонецЕсли;
// синхронизация по ID
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СсылкаНаОбъектДО
|ИЗ
| РегистрСведений.СвязиОбъектовИнтегрированныхСистем
|ГДЕ
| ТИПЗНАЧЕНИЯ(СсылкаНаОбъектДО) = ТИП(Справочник.Организации)
| И ИДВнешнегоОбъекта = &ИДВнешнегоОбъекта И (ТипВнешнегоОбъекта = &ТипВнешнегоОбъекта ИЛИ ТипВнешнегоОбъекта = """")";
Запрос.УстановитьПараметр("ИДВнешнегоОбъекта", ВнешнийID);
Запрос.УстановитьПараметр("ТипВнешнегоОбъекта", ВнешнийТип);
Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
Выборка = Результат.Выбрать();
Выборка.Следующий();
СсылкаДО = Выборка.СсылкаНаОбъектДО;
ОбъектДО = СсылкаДО.ПолучитьОбъект();
ОбъектДО.Заблокировать();
ЗаполнитьДанныеОрганизации(Узел, ОбъектДО, ОбъектXDTO);
Если ОбъектДО.Модифицированность() Тогда
ОбъектДО.Записать();
КонецЕсли;
Возврат ОбъектДО.Ссылка;
КонецЕсли;
// переопределяемый поиск
СсылкаДО = ОбработкаЗапросовXDTOПереопределяемый.НайтиСоответствиеДляВнешнегоОбъекта(ОбъектXDTO);
Если ЗначениеЗаполнено(СсылкаДО) Тогда
ОбъектДО = СсылкаДО.ПолучитьОбъект();
ОбъектДО.Заблокировать();
ЗаполнитьДанныеОрганизации(Узел, ОбъектДО, ОбъектXDTO);
Если ОбъектДО.Модифицированность() Тогда
ОбъектДО.Записать();
КонецЕсли;
Возврат ОбъектДО.Ссылка;
КонецЕсли;
// стандартный поиск
Наименование = "";
ИНН = "";
КПП = "";
ОбработкаЗапросовXDTO.ЗаполнитьРеквизитИзСвойстваXDTO(Узел, Наименование, ОбъектXDTO, "name");
ОбработкаЗапросовXDTO.ЗаполнитьРеквизитИзСвойстваXDTO(Узел, ИНН, ОбъектXDTO, "inn");
ОбработкаЗапросовXDTO.ЗаполнитьРеквизитИзСвойстваXDTO(Узел, КПП, ОбъектXDTO, "kpp");
Запрос = Новый Запрос;
ТекстЗапроса =
"ВЫБРАТЬ
| Организации.Ссылка
|ИЗ
| Справочник.Организации КАК Организации";
РезультатПустой = Истина;
Если ЗначениеЗаполнено(ИНН) И ЗначениеЗаполнено(КПП) Тогда
Запрос.Текст = ТекстЗапроса + " ГДЕ ИНН = &ИНН И КПП = &КПП ";
Запрос.УстановитьПараметр("ИНН", ИНН);
Запрос.УстановитьПараметр("КПП", КПП);
Результат = Запрос.Выполнить();
РезультатПустой = Результат.Пустой();
КонецЕсли;
Если РезультатПустой И ЗначениеЗаполнено(ИНН) Тогда
Запрос.Текст = ТекстЗапроса + " ГДЕ ИНН = &ИНН И Наименование = &Наименование ";
Запрос.УстановитьПараметр("ИНН", ИНН);
Запрос.УстановитьПараметр("Наименование", Наименование);
Результат = Запрос.Выполнить();
РезультатПустой = Результат.Пустой();
КонецЕсли;
Если РезультатПустой Тогда
Запрос.Текст = ТекстЗапроса + " ГДЕ Наименование = &Наименование ";
Запрос.УстановитьПараметр("Наименование", Наименование);
Результат = Запрос.Выполнить();
РезультатПустой = Результат.Пустой();
КонецЕсли;
Если РезультатПустой Тогда
ОбъектДО = Справочники.Организации.СоздатьЭлемент();
УстановитьСсылкуНовогоДляСправочника(ОбъектДО, ОбъектXDTO);
Иначе
Выборка = Результат.Выбрать();
Выборка.Следующий();
ОбъектДО = Выборка.Ссылка.ПолучитьОбъект();
КонецЕсли;
ОбъектДО.Заблокировать();
ЗаполнитьДанныеОрганизации(Узел, ОбъектДО, ОбъектXDTO);
Если ОбъектДО.Модифицированность() Тогда
ОбъектДО.Записать();
КонецЕсли;
Возврат ОбъектДО.Ссылка;
КонецФункции
Показать
Почему именно НайтиСоздатьОрганизацию()? Смотрим выше:
// Заполняет реквизит объекта информационной базы из свойства объекта XDTO
//
// Параметры:
// Узел - ПланОбменаСсылка.ИнтегрированныеСистемы - узел интегрированной системы
// Реквизит - Произвольный - реквизит объекта, подлежащий заполнению
// ОбъектXDTO - ОбъектXDTO - объект, свойство которого соответствует заполняемому реквизиту
// ИмяСвойстваXDTO - Строка - имя свойства, значение которого переносится в реквизит
//
Процедура ЗаполнитьРеквизитИзСвойстваXDTO(Узел, Реквизит, ОбъектXDTO, ИмяСвойстваXDTO) Экспорт
Если ОбъектXDTO.Установлено(ИмяСвойстваXDTO) Тогда
ЗначениеСвойстваXDTO = ОбъектXDTO[ИмяСвойстваXDTO];
Если ТипЗнч(ЗначениеСвойстваXDTO) = Тип("ОбъектXDTO") Тогда
Тип = ЗначениеСвойстваXDTO.objectId.type;
ID = ЗначениеСвойстваXDTO.objectId.id;
Если Не ЗначениеЗаполнено(ID) И Не ЗначениеЗаполнено(Тип) И ЗначениеСвойстваXDTO.Установлено("ExternalObject") Тогда
НовоеЗначение = НайтиСоздатьВнешнийОбъект(Узел, ЗначениеСвойстваXDTO);
Иначе
НовоеЗначение = ПолучитьСсылкуПоObjectID(ЗначениеСвойстваXDTO.objectId);
КонецЕсли;
Иначе
НовоеЗначение = ЗначениеСвойстваXDTO;
КонецЕсли;
Если Реквизит <> НовоеЗначение Тогда
Реквизит = НовоеЗначение;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Показать
Также прикладываю файл обмена. Стоит отметить, что данные IbjectID пустые (в отличие от того же самого внутреннего документа), а данных организации в РС "СвязиОбъектовИнтегрированныхСистем" нет, там лишь соответствия договоров. Видимо свойства договора все-же должны искаться по ключам. Но почему каждый раз перезаписываясь?!
Спасибо всем, кто откликнется.
Была похожая проблема, некогда было разбираться, просто закомментировала код перезаписи организации, т.к. у нас дополнительно к интеграции настроен обмен.
(2) У нас тоже, эта проблема и связана именно с обменом. Похоже, что отсюда выползает и ещё одна проблема - ЖР - он слишком быстрыми темпами "полнеет", и не удивительно, при таком то количестве перезаписей.
Написал обработку, в которой изменил алгоритм отправки данных, чтобы после каждого запроса удалялось сообщение в очереди.
Далее нашёл те, при которых происходила ошибка, удалил их и снова продолжил обмен.
Тем самым все сообщения были отправлены.
// Прочитывает данные из очереди на отправку и отправляет их в Документооборот.
//
Процедура ОтправитьДанные()
Попытка
ИдентификаторСообщения = Неопределено;
МоментВремени = Неопределено;
Запрос = Новый Запрос(
"ВЫБРАТЬ
| ОчередьСообщенийВ1СДокументооборот.МоментВремени КАК МоментВремени,
| ОчередьСообщенийВ1СДокументооборот.Данные КАК Данные,
| ОчередьСообщенийВ1СДокументооборот.Идентификатор КАК Идентификатор
|ИЗ
| РегистрСведений.ОчередьСообщенийВ1СДокументооборот КАК ОчередьСообщенийВ1СДокументооборот
|
|УПОРЯДОЧИТЬ ПО
| МоментВремени");
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
Возврат;
КонецЕсли;
Прокси = ИнтеграцияС1СДокументооборотПовтИсп.ПолучитьПрокси();
Запрос = ИнтеграцияС1СДокументооборот.СоздатьОбъект(Прокси, "DMPutChangesRequest");
ОбщийРазмерСообщений = 0;
ПредельныйРазмерСообщений =
ИнтеграцияС1СДокументооборотВызовСервера.МаксимальныйРазмерПередаваемогоФайла();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
ИдентификаторСообщения = Выборка.Идентификатор;
МоментВремени = Выборка.МоментВремени;
ДвоичныеДанные = Выборка.Данные.Получить();
//ОбщийРазмерСообщений = ОбщийРазмерСообщений + ДвоичныеДанные.Размер();
//Если ОбщийРазмерСообщений > ПредельныйРазмерСообщений
// И Запрос.objects.Количество() > 0 Тогда
//
// Результат = Прокси.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 = Неопределено;
#Если Сервер Тогда
УдалитьФайлы(ИмяФайлаСообщенияОбмена);
#КонецЕсли
Если Запрос.objects.Количество() > 0 Тогда
Результат = Прокси.execute(Запрос);
ИнтеграцияС1СДокументооборот.ПроверитьВозвратВебСервиса(Прокси, Результат);
КонецЕсли;
МенеджерЗаписи = РегистрыСведений.ОчередьСообщенийВ1СДокументооборот.СоздатьМенеджерЗаписи();
ЗаполнитьЗначенияСвойств(МенеджерЗаписи, Выборка);
МенеджерЗаписи.Удалить();
КонецЦикла;
//Если Запрос.objects.Количество() > 0 Тогда
// Результат = Прокси.execute(Запрос);
// ИнтеграцияС1СДокументооборот.ПроверитьВозвратВебСервиса(Прокси, Результат);
//КонецЕсли;
//Выборка.Сбросить();
//
//Пока Выборка.Следующий() Цикл
//
// МенеджерЗаписи = РегистрыСведений.ОчередьСообщенийВ1СДокументооборот.СоздатьМенеджерЗаписи();
// ЗаполнитьЗначенияСвойств(МенеджерЗаписи, Выборка);
// МенеджерЗаписи.Удалить();
//
//КонецЦикла;
Исключение
Инфо = ОписаниеОшибки();
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Интеграция с 1С:Документооборотом.Отправка данных'",
ОбщегоНазначенияКлиентСервер.КодОсновногоЯзыка()),
УровеньЖурналаРегистрации.Ошибка,
Метаданные.РегистрыСведений.ОчередьСообщенийВ1СДокументооборот,
Строка(ИдентификаторСообщения),
Запрос.Тип().Имя + Символы.ПС + Инфо);
Если ИдентификаторСообщения <> Неопределено Тогда
МенеджерЗаписи = РегистрыСведений.ОчередьСообщенийВ1СДокументооборот.СоздатьМенеджерЗаписи();
МенеджерЗаписи.МоментВремени = МоментВремени;
МенеджерЗаписи.Идентификатор = ИдентификаторСообщения;
МенеджерЗаписи.Прочитать();
МенеджерЗаписи.КоличествоПопытокОтправки = МенеджерЗаписи.КоличествоПопытокОтправки + 1;
МенеджерЗаписи.ТекстСообщенияОбОшибке = Инфо;
МенеджерЗаписи.Записать();
КонецЕсли;
КонецПопытки;
КонецПроцедуры
Показать
Изначально они копились в одном файле, пока не достигнув максимального размера, указанного в константе. Все же не стоило очищать стек только после отправки абсолютно всех данных. Или, возможно, уменьшение максимального размер пакета как-то помогло решить проблему. Стоит 10 мб.