Почему не сохраняется выбранное значение из списка выбора в 1С? Разбор ошибок и правильная реализация

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

При разработке интерфейсов в 1С (особенно на обычных формах) часто возникает задача: ограничить выбор пользователя динамическим списком, сформированным по определенным условиям. Например, показывать в поле ввода «Сотрудник» только тех работников, которые занимают определенную должность. Однако начинающие разработчики часто сталкиваются со следующей проблемой: список выбора заполняется корректно, пользователь видит нужные строки, нажимает на одну из них, но после выбора поле ввода на форме мгновенно очищается, а значение в документе не сохраняется.

В этой статье мы подробно разберем, почему возникает такая ситуация, проанализируем типичные ошибки в коде и рассмотрим правильные варианты реализации этой задачи в соответствии со стандартами разработки фирмы 1С.

В чем кроется основная причина ошибки?

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

Давайте посмотрим, как работает стандартный элемент управления формы типа «Поле ввода». Когда пользователь выбирает значение из выпадающего списка, платформа берет свойство Значение выбранного элемента списка и пытается сопоставить его с типом данных реквизита, к которому привязано это поле ввода:

Именно эта ошибка и произошла у автора темы на форуме. Давайте проанализируем его исходный код, выявим архитектурные недочеты и исправим их.

---

Анализ исходного кода и разбор ошибок

Посмотрим на код, который привел к неработающему интерфейсу:


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

Проанализируем эту процедуру детально. Здесь допущено сразу несколько серьезных ошибок разного уровня — от синтаксических до архитектурных.

Ошибка 1: Заполнение текстовыми строками вместо ссылок

В тексте запроса разработчик выбирает поле Сотрудники.Наименование, которое имеет тип Строка. Далее при заполнении списка выбора используется конструкция:

ЭлементыФормы.Сотрудник.СписокВыбора.Добавить(Строка(ВыборкаДетальныеЗаписи.Сотрудник));

Метод Добавить() для объекта СписокЗначений принимает параметры следующим образом: Добавить(<Значение>, <Представление>). Если передать только один параметр, платформа запишет его и в качестве технического значения, и в качестве текста на экране. Таким образом, в список выбора попадает обычная текстовая строка. При попытке выбора этой строки платформа пытается записать текст в ссылку, что закономерно приводит к очистке поля.

Ошибка 2: Бессмысленное приведение типов

Строка кода Наряд = Наряд.Ссылка; выглядит крайне странно. Если переменная Наряд изначально содержит ссылку на документ, то обращение к свойству Ссылка избыточно. Если же переменная содержала объект документа (что в контексте обработчика события формы маловероятно), то такое переопределения переменной "на лету" может нарушить логику работы формы.

Ошибка 3: Неподходящее событие для заполнения списка

Разработчик поместил код заполнения списка сотрудников в обработчик НарядПриИзменении. Это означает, что список сотрудников обновится только тогда, когда пользователь выберет или поменяет документ в поле Наряд. Если пользователь просто откроет существующий документ и решит сразу изменить сотрудника, список выбора окажется абсолютно пустым! Логика формирования списков выбора должна быть привязана к событиям самого поля ввода, а не сторонних реквизитов.

Ошибка 4: Жесткое кодирование (хардкод) в запросе

Фильтрация по строковому представлению должности: Сотрудники.Должность.Наименование = "Рабочий" — это грубое нарушение стандартов разработки 1С. Во-первых, если в справочнике должностей случайно переименуют «Рабочий» в «Рабочий (цех №1)» или поставят лишний пробел, программа перестанет работать. Во-вторых, обращение через точку к реквизитам связанных справочников снижает общую производительность системы.

---

Решение проблемы: Исправляем типы данных

Давайте исправим код так, чтобы в список выбора попадали ссылки, а пользователи видели понятные текстовые наименования. Для этого мы будем использовать перегрузку метода Добавить() с двумя параметрами:

  1. Значение — это техническая ссылка (СправочникСсылка.Сотрудники), которая запишется в базу данных.
  2. Представление — это строка (ФИО сотрудника), которую увидит пользователь на экране при раскрытии списка.

Рассмотрим правильный вариант запроса и заполнения списка:


// В запросе выбираем Ссылку и Наименование сотрудника
Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|    Сотрудники.Ссылка КАК Ссылка,
|    Сотрудники.Наименование КАК Наименование
|ИЗ
|    Справочник.Сотрудники КАК Сотрудники
|ГДЕ
|    Сотрудники.Должность = &ДолжностьРабочий";

// Передаем правильную ссылку на должность через параметры запроса вместо строки
Запрос.УстановитьПараметр("ДолжностьРабочий", Справочники.Должности.НайтиПоНаименованию("Рабочий"));

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

ЭлементыФормы.Сотрудник.СписокВыбора.Очистить();

Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    // Первым параметром передаем саму ССЫЛКУ, вторым - ее текстовое представление
    ЭлементыФормы.Сотрудник.СписокВыбора.Добавить(
        ВыборкаДетальныеЗаписи.Ссылка, 
        ВыборкаДетальныеЗаписи.Наименование
    );
КонецЦикла;

Теперь, когда пользователь выберет значение из списка, платформа сопоставит выбранную ссылку СправочникСсылка.Сотрудники с типом реквизита документа, и данные сохранятся без каких-либо проблем.

---

Архитектурное решение задачи по методике ИТС

Для того чтобы сделать интерфейс максимально надежным и быстрым, воспользуемся официальной методической поддержкой фирмы 1С. Правильный паттерн проектирования заключается в использовании события НачалоВыбора у самого элемента управления (поля ввода) Сотрудник.

Разберем по шагам, как это реализовать на обычных формах:

  1. Откроем свойства поля ввода Сотрудник на форме.
  2. Найдем событие НачалоВыбора и сгенерируем процедуру-обработчик на клиенте.
  3. Внутри обработчика отключим стандартное открытие формы выбора справочника (установим параметр СтандартнаяОбработка = Ложь).
  4. Заполним список значений актуальными данными прямо в момент нажатия на кнопку выбора.
  5. Вызовем метод формы ВыбратьИзСписка(), чтобы показать красивое выпадающее меню прямо под полем ввода.

Посмотрим на пример кода, написанного по стандартам ИТС:


Процедура СотрудникНачалоВыбора(Элемент, СтандартнаяОбработка)
    
    // Шаг 1. Отменяем стандартное открытие формы списка сотрудников
    СтандартнаяОбработка = Ложь;
    
    // Шаг 2. Создаем временный список для выбора
    СписокВыбора = Новый СписокЗначений;
    
    // Шаг 3. Заполняем его актуальными данными из базы данных
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    Сотрудники.Ссылка КАК Ссылка,
    |    Сотрудники.Наименование КАК Представление
    |ИЗ
    |    Справочник.Сотрудники КАК Сотрудники
    |ГДЕ
    |    НЕ Сотрудники.ПометкаУдаления"; // Пример простого условия на актуальность
    
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        СписокВыбора.Добавить(Выборка.Ссылка, Выборка.Представление);
    КонецЦикла;
    
    // Шаг 4. Показываем список пользователю "под" элементом формы
    ВыбранныйЭлемент = ВыбратьИзСписка(СписокВыбора, Элемент);
    
    // Шаг 5. Если пользователь сделал выбор, записываем значение в реквизит
    Если ВыбранныйЭлемент <> Неопределено Тогда
        // Записываем именно Значение (там хранится наша Ссылка)
        Сотрудник = ВыбранныйЭлемент.Значение; 
    КонецЕсли;

КонецПроцедуры

Как это реализовать на управляемых формах (1С 8.3)?

Если вы работаете в современном интерфейсе «Такси» на управляемых формах, то модальный метод ВыбратьИзСписка() использовать нельзя. Платформа 1С v8.3 требует асинхронного подхода.

На управляемых формах для решения этой задачи мы используем метод ПоказатьВыборИзСписка() с описанием оповещения. Посмотрим на этот пример:


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

КонецПроцедуры

&НаКлиенте
Процедура СотрудникЗавершениеВыбора(ВыбранныйЭлемент, ДополнительныеПараметры) Экспорт

    Если ВыбранныйЭлемент <> Неопределено Тогда
        // Записываем Ссылку из выбранного элемента в реквизит объекта
        Объект.Сотрудник = ВыбранныйЭлемент.Значение;
    КонецЕсли;

КонецПроцедуры

Проанализировав оба подхода, можно сделать вывод: правильное управление типами данных и выбор верных обработчиков событий платформы гарантируют быструю, стабильную работу интерфейса программы и исключают ошибки с потерей введенных данных.

← На главную