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