При работе с распределенными информационными базами (РИБ) разработчики иногда сталкиваются с ситуацией, когда во время приёма файла обмена выпадает ошибка вида «В данной транзакции уже происходили ошибки» или «Выбран договор контрагента, не соответствующий организации». В отладчике при этом видно, что один из реквизитов документа содержит значение ОбъектНенайден.
В этой статье разберем причинно-следственные связи возникновения подобных сбоев, проанализируем стандартные механизмы работы РИБ и рассмотрим архитектурно правильные способы проведения документов и передачи изменений конфигурации.
Стандартный механизм распределенной информационного базы в 1С устроен так, что платформа при формировании пакета выгрузки не гарантирует порядок следования объектов. Документ может быть записан в XML-файл раньше, чем элемент справочника (например, договор или номенклатура), на который он ссылается.
Это не является проблемой для штатного механизма загрузки РИБ по следующей причине: при нормальном прочтении файла методом ПланыОбмена.ПрочитатьИзменения() платформа записывает все объекты в режиме загрузки данных. При этом у всех записываемых объектов флаг ОбменДанными.Загрузка автоматически устанавливается в значение Истина.
Посмотрим, как выглядит стандартная проверка в модуле объекта (например, в событии ПередЗаписью):
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Если данные загружаются через обмен, отключаем прикладные проверки и проведение
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Прикладные проверки и логика заполнения
СтруктураДействий = Новый Структура("УстановитьДоговор");
УправлениеДенежнымиСредствами.ВыполнитьДействияПередЗаписьюПлатежногоДокумента(ЭтотОбъект, СтруктураДействий, Отказ, РежимЗаписи, РежимПроведения);
КонецПроцедуры
Если этот флаг установлен, система записывает документ «как есть», без выполнения бизнес-логики, проведения и проверок заполнения реквизитов. Ссылка на договор записывается на уровне уникального идентификатора (GUID). Даже если сам договор идет дальше по файлу, он запишется на следующих шагах транзакции, и ссылка «склеится» без каких-либо ошибок.
Ошибка появляется тогда, когда в процессе обмена вручную сбрасывается флаг ОбменДанными.Загрузка = Ложь или принудительно устанавливается режим записи в РежимЗаписиДокумента.Проведение (например, в обработчике события плана обмена ПриПолученииДанныхОтГлавного или подписках на события).
В этот момент 1С пытается выполнить полноценное проведение документа. Так как связанный справочник еще не прочитан из файла XML и не существует в базе, реквизит содержит ОбъектНенайден. Проведение завершается ошибкой, транзакция блокируется, и весь обмен останавливается.
Если по бизнес-логике документы, прилетающие из центрального узла или филиала, обязательно должны быть проведены в подчиненной базе, нельзя проводить их непосредственно в момент чтения сообщения обмена.
Проанализируем правильный алгоритм обработки таких документов:
ПланыОбмена.ПрочитатьИзменения() сохраняем документы в базовом режиме (с установленным флагом ОбменДанными.Загрузка = Истина).ПриПолученииДанныхОтГлавного или в подписке регистрируем ссылки на прилетевшие документы в специальном регистре сведений (например, «Очередь отложенного проведения»).ПланыОбмена.ПрочитатьИзменения() успешно завершил работу и зафиксировал транзакцию, запускаем отдельную процедуру (или фоновое задание), которая выбирает документы из очереди и проводит их.К моменту запуска отложенного проведения транзакция обмена уже завершена, все элементы справочников и классификаторов успешно записаны в базу, поэтому битых ссылок при проведении не возникнет.
Если в подчиненной базе требуется не просто проводить входящие документы, а трансформировать их (например, превращать РеализациюТоваровУслуг головного офиса в ПоступлениеТоваровУслуг филиала), целесообразно разделить транспортные каналы:
Рассмотрим случай, когда выгрузка данных выполняется программно с использованием объектов платформы. Чтобы в файл обмена корректно попадали изменения конфигурации, выгрузка должна формироваться с помощью объекта ЗаписьСообщенияСвертки / ЗаписьСообщения и менеджера планов обмена.
Пример стандартного программного кода выгрузки данных РИБ:
// Создаем запись XML и формируем файл обмена
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяФайлаОбмена);
// Создаем новое сообщение обмена
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, УзелОбмена);
// Выгружаем зарегистрированные изменения (включая изменения конфигурации)
ПланыОбмена.ЗаписатьИзменения(ЗаписьСообщения, КоличествоЭлементовВТранзакции);
// Завершаем запись
ЗаписьСообщения.ЗакончитьЗапись();
ЗаписьXML.Закрыть();
При вызове метода ПланыОбмена.ЗаписатьИзменения() платформа сама проверяет, есть ли зарегистрированные изменения конфигурации для данного узла, и помещает их в заголовок файла обмена перед данными.
На стороне приемника при прочтении файла с изменениями конфигурации необходимо обрабатывать специальный флаг:
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайлаОбмена);
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
КонфигурацияИзменена = Ложь;
// Чтение изменений из сообщения РИБ
ПланыОбмена.ПрочитатьИзменения(ЧтениеСообщения, КоличествоЭлементовВТранзакции, КонфигурацияИзменена);
ЧтениеСообщения.ЗакончитьЧтение();
ЧтениеXML.Закрыть();
Если КонфигурацияИзменена Тогда
// Изменения конфигурации приняты, требуется обновить конфигурацию базы данных
// Завершаем сеанс или вызываем обновление через завершение работы системы
КонецЕсли;
Если параметр КонфигурацияИзмененаПриОбменеРИБ принимает значение Истина, данные конфигурации помещаются во временное состояние. После завершения чтения сообщения необходимо обновить конфигурацию базы данных (например, перезапустив сеанс с ключом /UpdateDBCfg) и только после этого продолжать работу с новыми объектами.
Для стабильной работы РИБ необходимо соблюдать базовое правило: никогда не сбрасывать ОбменДанными.Загрузка = Истина в момент выполнения метода ПрочитатьИзменения(). Любая логика проведения документов, проверки бизнес-правил или создания подчиненных объектов должна выноситься на этап отложенной обработки после полной фиксации транзакции загрузки.