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

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

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

Рассмотрим подробнее, как реализовать передачу контекста в обычном приложении, проанализируем возможные трудности и разберем проверенные способы решения этой задачи.

Метод 1: Обработка отборов в событии ПередНачаломДобавления

Если форма списка открыта с установленным отбором, платформа 1С автоматически пытается заполнить реквизиты нового объекта значениями из этого отбора. Но это работает только для вида сравнения Равно. Если же отбор установлен списком (ВидСравнения.ВСписке), система «теряется» и оставляет реквизит пустым.

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

Проанализируем пример кода, который решает эту проблему:


Процедура СписокПередНачаломДобавления(Элемент, Отказ, Копирование, Пояснение, СтандартнаяОбработка)
    
    // Если происходит копирование, стандартная логика обычно устраивает
    Если Копирование Тогда
        Возврат;
    КонецЕсли;

    // Остановим стандартную обработку, чтобы создать объект вручную с нашими данными
    СтандартнаяОбработка = Ложь;
    
    НовыйЭлементОбъект = Справочники[Метаданные().Имя].СоздатьЭлемент();
    
    // Проходим по всем установленным отборам в форме списка
    Для Каждого ЭлементОтбора Из Список.Отбор Цикл
        Если ЭлементОтбора.Использование И ЗначениеЗаполнено(ЭлементОтбора.Значение) Тогда
            
            // Обрабатываем случай, когда отбор задан списком
            Если ЭлементОтбора.ВидСравнения = ВидСравнения.ВСписке Тогда
                СписокЗначенийДляВыбора = ЭлементОтбора.Значение;
                ВыбранныйЭлемент = СписокЗначенийДляВыбора.ВыбратьЭлемент("Выберите значение для заполнения");
                
                Если ВыбранныйЭлемент <> Неопределено Тогда
                    ИмяРеквизита = ЭлементОтбора.ПутьКДанным; // Или ЭлементОтбора.Имя
                    НовыйЭлементОбъект[ИмяРеквизита] = ВыбранныйЭлемент.Значение;
                КонецЕсли;
            ИначеЕсли ЭлементОтбора.ВидСравнения = ВидСравнения.Равно Тогда
                НовыйЭлементОбъект[ЭлементОтбора.Имя] = ЭлементОтбора.Значение;
            КонецЕсли;
            
        КонецЕсли;
    КонецЦикла;

    // Открываем форму нового элемента с уже заполненными данными
    ФормаЭлемента = НовыйЭлементОбъект.ПолучитьФорму();
    ФормаЭлемента.Открыть();

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

В этом примере мы перехватываем создание объекта и вручную переносим данные из коллекции Отбор в реквизиты нового объекта. Это гарантирует, что даже при сложных фильтрах данные не будут потеряны.

Метод 2: Использование скрытых параметров или свойств элементов управления

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

Один из нестандартных, но работающих способов в обычном приложении — использование свойства Подсказка (Tooltip) элемента управления, который инициировал выбор. Выясним причину популярности этого метода: в обычном приложении свойство ВладелецФормы у формы списка может указывать на элемент управления (поле ввода) документа.

Рассмотрим реализацию по шагам:

  1. В форме документа: Перед открытием формы списка мы записываем ссылку на текущий документ в подсказку поля выбора.
  2. В форме списка справочника: При открытии или при добавлении нового элемента считываем эту строку и преобразуем её обратно в ссылку.

Пример реализации передачи данных через строку (сериализация ссылки):


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

// В форме списка справочника (событие ПриОткрытии или ПередНачаломДобавления)
Процедура ПриОткрытии()
    ЭлементУправления = ЭтаФорма.ВладелецФормы; 
    Если ТипЗнч(ЭлементУправления) = Тип("ПолеВвода") Тогда
        ТекстПодсказки = ЭлементУправления.Подсказка;
        ПозицияМаркера = Найти(ТекстПодсказки, "$Link$");
        
        Если ПозицияМаркера > 0 Тогда
            // Извлекаем и десериализуем ссылку на документ
            СсылкаНаДокумент = ЗначениеИзСтрокиВнутр(Сред(ТекстПодсказки, ПозицияМаркера + 6));
            // Теперь у нас есть контекст документа в форме списка!
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Важно: Этот метод является «хаком» и должен использоваться с осторожностью. Более правильным подходом в обычном приложении будет добавление в форму списка экспортной переменной ДокументОснование, которую нужно заполнять сразу после создания формы списка программно.

Метод 3: Работа через процедуру ОбработкаЗаполнения

Выясним, как использовать штатный механизм ОбработкаЗаполнения, который находится в модуле объекта справочника. Этот метод наиболее универсален и поддерживается как в обычном, так и в управляемом приложении.

Когда мы вызываем метод Заполнить(ДанныеЗаполнения), управление передается в модуль объекта. В качестве ДанныеЗаполнения может выступать не только ссылка, но и структура.

Проанализируем ситуацию: мы хотим создать элемент из формы списка и передать в него структуру данных. Для этого в форме списка (в том же событии ПередНачаломДобавления) мы пишем следующее:


СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("ДокументОснование", СсылкаНаДокумент);
СтруктураДанных.Вставить("Склад", ТекущийСклад);

НовыйОбъект = Справочники.МойСправочник.СоздатьЭлемент();
НовыйОбъект.Заполнить(СтруктураДанных); // Вызов модуля объекта
НовыйОбъект.ПолучитьФорму().Открыть();

Теперь в модуле объекта справочника мы должны обработать эти данные:


Процедура ОбработкаЗаполнения(ДанныеЗаполнения)
    
    Если ТипЗнч(ДанныеЗаполнения) = Тип("Структура") Тогда
        // Заполняем реквизиты из структуры
        Если ДанныеЗаполнения.Свойство("ДокументОснование") Тогда
            РеквизитДокумент = ДанныеЗаполнения.ДокументОснование;
        КонецЕсли;
        
        Если ДанныеЗаполнения.Свойство("Склад") Тогда
            Склад = ДанныеЗаполнения.Склад;
        КонецЕсли;
    ИначеЕсли ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.МойДокумент") Тогда
        // Если передана просто ссылка
        РеквизитДокумент = ДанныеЗаполнения;
    КонецЕсли;
    
КонецПроцедуры

Этот подход считается наиболее архитектурно правильным в 1С. Мы отделяем логику интерфейса (откуда берутся данные) от логики объекта (как эти данные должны быть обработаны при создании).

Резюме и рекомендации

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

  1. Если вам нужно просто подхватить значение из отборов — используйте Метод 1. Он требует минимум правок и понятен пользователю.
  2. Если контекст сложный и данные нужно передать «незаметно» через интерфейс — можно использовать Метод 2 (или экспортные переменные формы).
  3. Для системного решения, которое будет работать надежно и прозрачно — всегда выбирайте Метод 3 с использованием структуры в ОбработкаЗаполнения.

Помните, что в управляемых формах (v8.3) передача параметров осуществляется еще проще через параметр формы ЗначенияЗаполнения, который автоматически пробрасывается в ОбработкаЗаполнения без лишнего кода в интерфейсе. Однако для обычного приложения вышеописанные способы остаются основными инструментами разработчика.

← На главную