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

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке расширений или доработке конфигураций на платформе 1С:Предприятие 8.3 часто возникает задача создания специализированных форм подбора. Например, мы хотим открыть из документа «Реализация товаров и услуг» свою особую форму, найти или создать там номенклатуру, а затем добавить эту позицию в табличную часть исходного документа.

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

Почему не работает прямой доступ к объекту?

Рассмотрим типичную ошибку. Разработчик создает новую форму, добавляет туда реквизит Объект и пытается программно обратиться к нему, чтобы добавить строку в товары.

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

Как метко заметили в обсуждении, это похоже на поход в магазин:

Вы вышли из дома (форма документа) и пошли в магазин (форма подбора). Когда вы купили товар, вы не идете строить новый дом, чтобы положить туда покупки. Вы возвращаетесь в свой старый дом с товаром.

Попытка использовать РеквизитФормыВЗначение("Объект") во второй форме создает в памяти копию объекта базы данных (или совершенно новый объект), который никак не связан с тем документом, который видит пользователь на экране в первой форме.

Правильное решение: Механизм оповещения о закрытии

Чтобы добавить строку в документ, мы не должны пытаться «залезть» в него из внешней формы. Вместо этого мы должны вернуть найденную номенклатуру из формы подбора, а саму вставку строки произвести уже в модуле формы документа.

Давайте реализуем это пошагово.

Шаг 1. Открытие формы подбора из документа

В форме документа «Реализация товаров и услуг» (в команде вашей кнопки) мы будем использовать конструктор ОписаниеОповещения. Это инструкция для платформы: «какую процедуру выполнить, когда вспомогательная форма закроется».


&НаКлиенте
Процедура ПодобратьТовар(Команда)
    
    // Создаем описание оповещения. 
    // "ОбработатьВыборТовара" - это имя процедуры в этой же форме, которая сработает после закрытия окна подбора.
    Оповещение = Новый ОписаниеОповещения("ОбработатьВыборТовара", ЭтотОбъект);
    
    // Открываем нашу форму из расширения.
    // Параметры можно передать структурой (если нужно), владелец - ЭтаФорма.
    ОткрытьФорму("Документ.РеализацияТоваровУслуг.Форма.ФормаПодбораСпец", , ЭтаФорма, , , , Оповещение);
    
КонецПроцедуры

Шаг 2. Возврат данных из формы подбора

Теперь перейдем в нашу произвольную форму (в расширении), где пользователь выбирает или создает номенклатуру. Допустим, у нас есть кнопка «Перенести в документ».

В обработчике этой кнопки мы не пишем код добавления строк. Мы просто закрываем форму и передаем в метод Закрыть() нужные данные. Обычно передают структуру.


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

Шаг 3. Обработка результата в документе

Вернемся в модуль формы документа «Реализация товаров и услуг». Нам нужно описать ту самую экспортную процедуру, которую мы указали в ОписаниеОповещения.


&НаКлиенте
Процедура ОбработатьВыборТовара(РезультатЗакрытия, ДополнительныеПараметры) Экспорт
    
    // Проверяем, что результат не неопределен (пользователь мог просто закрыть окно крестиком)
    Если РезультатЗакрытия = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // РезультатЗакрытия - это та самая структура, которую мы передали в методе Закрыть()
    
    // Добавляем строку в табличную часть.
    // Обратите внимание: Объект.Товары доступен на клиенте для чтения и добавления строк!
    НоваяСтрока = Объект.Товары.Добавить();
    НоваяСтрока.Номенклатура = РезультатЗакрытия.Номенклатура;
    НоваяСтрока.Количество = РезультатЗакрытия.Количество;
    НоваяСтрока.Цена = РезультатЗакрытия.Цена;
    НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;
    
    // Если нужно пересчитать суммы документа, можно вызвать серверный метод
    // РассчитатьСуммуДокументаНаСервере(); 
    
КонецПроцедуры

Альтернативный вариант: Через владельца формы

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

При открытии формы мы указали владельца: ЭтаФорма. В открытой форме мы можем обратиться к этому владельцу через свойство ЭтаФорма.Владелец.

Пример кода во второй форме (форме подбора), если мы хотим сразу писать в родитель:


&НаКлиенте
Процедура ПеренестиВДокументНапрямую(Команда)
    
    // Получаем форму-владельца
    ФормаВладелец = ЭтаФорма.Владелец;
    
    // Проверяем, что владелец существует и у него есть Объект
    Если ФормаВладелец <> Неопределено И ТипЗнч(ФормаВладелец) = Тип("УправляемаяФорма") Then
        
        // Обращаемся к данным владельца
        // ВАЖНО: Это сработает, только если данные доступны на клиенте
        НоваяСтрока = ФормаВладелец.Объект.Товары.Добавить();
        НоваяСтрока.Номенклатура = Объект.Номенклатура;
        
        // Оповещаем владельца об изменении данных (чтобы загорелась звездочка модифицированности)
        ФормаВладелец.Модифицированность = Истина;
        
        Закрыть();
        
    КонецЕсли;
    
КонецПроцедуры

Почему первый способ лучше?

  1. Независимость: Форма подбора не обязана знать структуру формы-владельца. Она просто возвращает результат ("Вот товар, делай с ним что хочешь").
  2. Безопасность: Меньше риска возникновения ошибок при обновлении конфигурации, если в родительской форме изменятся имена реквизитов.
  3. Асинхронность: Модель ОписаниеОповещения соответствует стандартам разработки для веб-клиента и мобильных приложений 1С.

Резюме

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

  1. В документе вызывайте ОткрытьФорму с параметром ОписаниеОповещения.
  2. Во внешней форме собирайте данные в Структуру и возвращайте их через Закрыть(Структура).
  3. В обработчике оповещения (в документе) принимайте структуру и добавляйте строку в Объект.Товары.
← На главную