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