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