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