Как получить данные из табличной части подчиненного справочника на форму в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Часто при разработке в 1С возникает задача: при выборе определенного значения на форме (например, клиента), необходимо автоматически заполнить другие поля данными, которые хранятся в связанных справочниках или их табличных частях. Разберем подобную ситуацию детально.

Суть задачи: На форме документа или элемента справочника есть реквизит выбора клиента. В системе существует справочник Клиенты, а также подчиненный ему справочник КонтактныеЛица. У контактных лиц есть табличная часть КонтактнаяИнформация, где хранится электронная почта. Нам нужно при изменении контрагента на форме подтянуть его почту из табличной части контактного лица на форму.

Проанализируем типичную ошибку начинающих

Многие разработчики прибегают к конструктору запросов и получают примерно следующий код, который мы видели в обсуждении проблемы:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    КонтактныеЛицаКонтактнаяИнформация.АдресЭП КАК АдресЭП
    |ИЗ
    |    Справочник.КонтактныеЛица.КонтактнаяИнформация КАК КонтактныеЛицаКонтактнаяИнформация";
    
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
    
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    Почта = ВыборкаДетальныеЗаписи.АдресЭП;
    Возврат Почта;
КонецЦикла;

Главная проблема этого кода в том, что запрос получает абсолютно все записи из таблицы базы данных. В нем отсутствует условие отбора по конкретному клиенту. В результате система всегда будет брать одну и ту же (первую в выборке) почту при выборе совершенно разных контрагентов.

Напишем правильный запрос с параметрами

Чтобы отфильтровать данные по конкретному клиенту, нам необходимо использовать секцию ГДЕ и передать параметр в запрос. Поскольку табличная часть принадлежит справочнику КонтактныеЛица, вероятнее всего, его владельцем является справочник Клиенты. Условие должно обращаться к владельцу ссылки.

Также важно помнить, что в типовых табличных частях контактной информации могут храниться телефоны, фактические адреса и прочее. Нам нужно сделать строгий отбор по типу информации, иначе запрос вернет непредсказуемый результат.

Посмотрим на правильный вариант запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |    КонтактныеЛицаКонтактнаяИнформация.АдресЭП КАК Почта
    |ИЗ
    |    Справочник.КонтактныеЛица.КонтактнаяИнформация КАК КонтактныеЛицаКонтактнаяИнформация
    |ГДЕ
    |    КонтактныеЛицаКонтактнаяИнформация.Ссылка.Владелец = &Клиент
    |    И КонтактныеЛицаКонтактнаяИнформация.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты)";

// Передаем конкретного выбранного клиента в параметр запроса
Запрос.УстановитьПараметр("Клиент", ВыбранныйКлиент);

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Если Выборка.Следующий() Тогда
    Возврат Выборка.Почта;
Иначе
    Возврат ""; // Почта не найдена
КонецЕсли;

Использование конструкции ВЫБРАТЬ ПЕРВЫЕ 1 здесь необходимо. Оно гарантирует, что система возьмет только один (первый попавшийся) адрес электронной почты, если у контрагента заведено несколько email-адресов. Это предотвратит избыточность данных.

Разберем правильное разделение контекста: Клиент и Сервер

Событие формы ПриИзменении, которое срабатывает при выборе контрагента пользователем, выполняется на стороне клиента (директива &НаКлиенте). Однако выполнение запросов к базе данных возможно только на сервере. Поэтому логику необходимо разделить на две процедуры.

Рассмотрим, как грамотно реализовать это в модуле формы:


&НаКлиенте
Процедура КлиентПриИзменении(Элемент)
    // Вызываем серверную функцию для получения почты, передавая ссылку на клиента
    Почта = ПолучитьПочтуКлиентаНаСервере(Объект.Клиент);
    Объект.Почта = Почта; // Заполняем соответствующий реквизит на форме
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьПочтуКлиентаНаСервере(КлиентСсылка)
    // Здесь располагается код запроса
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |    КонтактныеЛицаКонтактнаяИнформация.АдресЭП КАК Почта
        |ИЗ
        |    Справочник.КонтактныеЛица.КонтактнаяИнформация КАК КонтактныеЛицаКонтактнаяИнформация
        |ГДЕ
        |    КонтактныеЛицаКонтактнаяИнформация.Ссылка.Владелец = &Клиент
        |    И КонтактныеЛицаКонтактнаяИнформация.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты)";
        
    Запрос.УстановитьПараметр("Клиент", КлиентСсылка);
    
    Выборка = Запрос.Выполнить().Выбрать();
    
    Возврат ?(Выборка.Следующий(), Выборка.Почта, "");
КонецФункции

Обратите особое внимание на использование директивы &НаСервереБезКонтекста. В данном случае это самое оптимальное решение, так как для выполнения запроса и получения одной строки нам не нужно "гонять" весь контекст формы (все данные, находящиеся на ней) с клиента на сервер и обратно.

Альтернативное решение: использование библиотеки стандартных подсистем (БСП)

Если ваша разработка ведется на базе любой современной типовой конфигурации 1С (ERP, Управление торговлей, Бухгалтерия предприятия, УНФ и другие), то писать прямые запросы к табличным частям контактной информации строго не рекомендуется.

Вместо этого разработчики фирмы 1С используют готовые программные интерфейсы БСП. Для работы с контактной информацией существует специальный общий модуль УправлениеКонтактнойИнформацией (в старых релизах конфигураций он мог называться просто КонтактнаяИнформация).

Посмотрим, как изящно можно получить почту всего одной строкой кода:


Почта = УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъекта(СсылкаНаКонтактноеЛицо, Справочники.ВидыКонтактнойИнформации.EmailКонтактныеЛица);

Почему этот подход считается лучшим?

  1. Использование типовых функций БСП защищает ваш код от поломок при обновлениях конфигурации. Если фирма 1С решит изменить структуру таблиц или логику хранения контактной информации, ваша доработка продолжит работать без вмешательства.
  2. Код становится намного более компактным и легко читаемым.
  3. Вам не нужно вручную прописывать отборы по типам или видам контактной информации — типовая функция содержит всю необходимую логику внутри.

Таким образом, мы детально разобрали как классический технический подход с использованием языка запросов, так и современный, безопасный метод с применением стандартов БСП. Всегда старайтесь анализировать окружение конфигурации перед написанием кода.

← На главную