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