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