Как реализовать заполнение документа по кнопке и исправить ошибку «Переменная не определена» в 1С

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

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

Разбор ошибки: Обращение к табличной части в модуле формы

Первая проблема, с которой сталкивается автор темы, — попытка обратиться к табличной части документа напрямую по её имени: Заказы.Добавить(). Проанализируем ситуацию: в управляемом приложении код модуля формы работает с данными формы, а не напрямую с объектом базы данных. Все реквизиты документа (включая табличные части) «спрятаны» внутри основного реквизита формы, который обычно называется Объект.

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


// Неправильно:
НоваяСтрока = Заказы.Добавить(); 

// Правильно:
НоваяСтрока = Объект.Заказы.Добавить();

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

Проблема контекста: Почему «Тип не определен (ДокументОбъект)»?

Вторая и более серьезная ошибка связана с непониманием разницы между Клиентом и Сервером. Автор пытается выполнить проверку типа ДокументОбъект.БанковскаяВыписка на клиенте или в процедуре, которая не имеет доступа к серверным типам данных.

Выясним причину: тип ДокументОбъект доступен только на сервере. На стороне клиента мы можем оперировать только ссылками (ДокументСсылка). Если вы пишете условие ТипЗнч(Данные) = Тип("ДокументОбъект...") внутри процедуры с директивой &НаКлиенте, платформа выдаст ошибку, так как клиентское приложение «не знает», что такое объект документа — оно знает только его ссылку.

Проанализируем ситуацию с кнопкой «Заполнить». Если мы хотим заполнить данные на основании другого документа, нам нужно:</1>

  1. Выбрать документ-основание на клиенте.
  2. Передать ссылку на него на сервер.
  3. На сервере преобразовать данные формы в реальный объект, выполнить заполнение и вернуть данные обратно на форму.

Решение №1: Прямое заполнение через серверный контекст формы

Рассмотрим вариант реализации кнопки «Заполнить», если логика заполнения пишется непосредственно в модуле формы. Предположим, у нас есть реквизит на форме ДокументОснование, из которого мы хотим перенести данные в текущий документ РасходнаяНакладная.


&НаКлиенте
Процедура КомандаЗаполнить(Команда)
    // Вызываем серверную процедуру для работы с объектом
    ЗаполнитьНаСервере();
КонецПроцедуры

&НаСервере
Процедура ЗаполнитьНаСервере()
    // 1. Превращаем данные формы (Объект) в прикладной объект (ДокументОбъект)
    // Это нужно, чтобы получить доступ к методам объекта
    ТекущийОбъект = РеквизитФормыВЗначение("Объект");
    
    // 2. Получаем данные из источника (например, из ссылки, выбранной на форме)
    // Допустим, ссылка хранится в переменной Объект.ДокументОснование
    Если ЗначениеЗаполнено(Объект.ДокументОснование) Тогда
        Источник = Объект.ДокументОснование; // Это ДокументСсылка
        
        // Очищаем текущую табличную часть перед заполнением
        ТекущийОбъект.Заказы.Очистить();
        
        // Переносим данные из источника
        Для Каждого СтрокаИсточника Из Источник.Заказы Цикл
            НоваяСтрока = ТекущийОбъект.Заказы.Добавить();
            ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИсточника);
        КонецЦикла;
        
        // Копируем реквизиты шапки
        ТекущийОбъект.Клиент = Источник.Контрагент;
    КонецЕсли;
    
    // 3. Самый важный шаг: записываем изменения обратно в реквизит формы
    // Без этого данные на экране не обновятся!
    ЗначениеВДанныеФормы(ТекущийОбъект, Объект);
КонецПроцедуры

В данном примере мы использовали функцию ЗаполнитьЗначенияСвойств. Это очень удобный метод 1С, который автоматически сопоставляет поля с одинаковыми именами в источнике и приемнике. Это избавляет от необходимости писать НоваяСтрока.Товар = СтрокаИсточника.Товар для каждого поля.

Решение №2: Вызов штатного механизма «ОбработкаЗаполнения»

Наиболее правильным и профессиональным подходом считается использование встроенного механизма ОбработкаЗаполнения, который находится в Модуле объекта. Это позволяет избежать дублирования кода: одна и та же логика будет работать и при создании «на основании», и при нажатии кнопки «Заполнить».

Посмотрим на пример реализации в модуле объекта (РасходнаяНакладная):


// В модуле объекта
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения, СтандартнаяОбработка)
    Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.БанковскаяВыписка") Тогда
        // Здесь пишем логику заполнения
        ЭтотОбъект.Клиент = ДанныеЗаполнения.Клиент;
        ЭтотОбъект.Заказы.Загрузить(ДанныеЗаполнения.Заказы.Выгрузить());
    КонецЕсли;
КонецПроцедуры

Теперь в модуле формы для кнопки «Заполнить» нам достаточно написать всего несколько строк кода, чтобы запустить этот механизм:


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

Преимущества этого способа:

  1. Логика заполнения хранится в одном месте (в модуле объекта).
  2. Метод Загрузить() работает быстрее, чем перебор строк в цикле.
  3. Код формы остается чистым и коротким.

Типичные ловушки при передаче данных

Разберем ситуацию из сообщения №27 форума. Автор пытается использовать ДанныеФормыВЗначение(ОснРеквизит, Тип("ДокументОбъект.БанковскаяВыписка")). Выясним, почему это приводит к ошибке Недопустимое значение параметра (параметр номер '1').

Функция ДанныеФормыВЗначение ожидает в первом параметре структуру данных текущей формы. Если ОснРеквизит — это весь Объект (РасходнаяНакладная), то вы не можете преобразовать его в тип БанковскаяВыписка. Это разные типы метаданных. Нельзя превратить «яблоко» в «апельсин» просто указав другой тип в параметре. Чтобы получить данные «Банковской выписки», её нужно либо прочитать из базы по ссылке (на сервере), либо передать её реквизиты отдельно.

Подведем итог:

← На главную