При разработке внешних обработок в среде 1С:Предприятие 8 часто возникает задача автоматического создания и заполнения документов на основании других объектов. Типичная ошибка начинающих разработчиков — попытка получить форму создаваемого документа и напрямую вызвать из модуля этой формы процедуру заполнения (например, заполнение табличной части). В результате программа выдает ошибку «Метод объекта не обнаружен», а разработчик задается вопросом: почему экспортный метод формы недоступен извне и как правильно реализовать эту задачу по стандартам фирмы «1С»?
В этой статье мы подробно разберем причины возникновения этой проблемы, проанализируем архитектурные стандарты платформы 1С v8.3 и рассмотрим три корректных способа программного создания и заполнения документов без прямого обращения к контексту формы.
Попытка вызвать процедуру управляемой или обычной формы из внешнего кода (например, из модуля внешней обработки) чаще всего заканчивается аварийным завершением работы программы. Давайте выясним причины такого поведения платформы 1С:
&НаСервере или вообще не имеет директивы (что по умолчанию приравнивает её к серверной), то вызвать её напрямую из клиентского кода внешней обработки невозможно.Экспорт. Даже если вы получили объект формы через метод ПолучитьФорму(), вызвать из него можно только те процедуры и функции, которые явно объявлены как экспортируемые с помощью ключевого слова Экспорт. Если ключевое слово отсутствует, процедура остается приватной областью видимости модуля формы.ДокументОбъект) и форма документа (тип УправляемаяФорма) — это абсолютно разные программные сущности. Попытка решать задачи заполнения данных через интерфейсные методы формы приводит к сильной связанности кода и делает невозможным выполнение этих операций в фоновых заданиях, где интерфейс отсутствует в принципе.Перейдем от теории к практике и разберем правильные архитектурные решения этой задачи.
Согласно стандартам разработки «1С», вся бизнес-логика (включая расчеты, заполнение реквизитов и табличных частей на основании других документов) должна находиться в модуле объекта или модуле менеджера документа, но никак не в модуле формы. Для реализации заполнения по умолчанию в модуле объекта каждого документа предусмотрен стандартный предопределенный обработчик события ОбработкаЗаполнения.
Давайте рассмотрим, как правильно и элегантно написать код во внешней обработке. Нам вообще не потребуется получать форму документа. Мы создадим объект на сервере, вызовем стандартный метод платформы Заполнить() и запишем проведенный документ:
// Код выполняется в серверном контексте внешней обработки
Процедура СоздатьДокументУстЦенНоменклатуры(СсылкаДокОснование) Экспорт
// Создаем новый объект документа программно в памяти сервера
НовыйДок = Документы.УстановкаЦенНоменклатуры.СоздатьДокумент();
НовыйДок.Дата = ТекущаяДата();
// Вызываем заполнение шапки стандартными механизмами конфигурации
ЗаполнениеДокументов.ЗаполнитьШапкуДокумента(НовыйДок, глЗначениеПеременной("глТекущийПользователь"));
НовыйДок.НеПроводитьНулевыеЗначения = Истина;
// ВАЖНО: Платформа автоматически вызывает событие "ОбработкаЗаполнения" в модуле объекта.
// В качестве параметра передается документ-основание.
НовыйДок.Заполнить(СсылкаДокОснование);
// Записываем документ с проведением
Попытка
НовыйДок.Записать(РежимЗаписиДокумента.Проведение);
Сообщить("Документ успешно создан и проведен: " + СокрЛП(НовыйДок.Ссылка));
Исключение
Сообщить("Не удалось провести документ! Ошибка: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
В этом сценарии метод платформы Заполнить() передает управление в модуль объекта создаваемого документа УстановкаЦенНоменклатуры, где разработчики конфигурации уже реализовали всю необходимую логику анализа документа-основания и заполнения табличной части ценами. Этот подход гарантирует стабильную работу кода в любых условиях, включая регламентные задания, Web-сервисы и интеграции.
Рассмотрим вторую ситуацию: вам необходимо программно создать заполненный документ, но не записывать его сразу в базу данных, а открыть его форму на экране, чтобы пользователь мог проверить данные, скорректировать их и нажать кнопку «Провести» самостоятельно.
В управляемом приложении категорически запрещено открывать форму, а затем пытаться интерактивно заполнять её элементы из внешней обработки. Вместо этого мы должны использовать механизм параметров формы при её открытии. Давайте разберем этот процесс по шагам:
Основание или ЗначениеЗаполнения.ОткрытьФорму(), куда передаются эти параметры.ОбработкаЗаполнения в модуле объекта еще до момента вывода формы на экран.Посмотрим на пример кода во внешней обработке на клиенте:
&НаКлиенте
Процедура ОткрытьФормуЗаполненияНаОсновании(СсылкаДокОснование)
// Формируем параметры для открываемой формы
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("Основание", СсылкаДокОснование);
// Открываем форму документа. Платформа сама создаст новый объект и заполнит его
ОткрытьФорму("Документ.УстановкаЦенНоменклатуры.Форма.ФормаДокумента", ПараметрыФормы);
КонецПроцедуры
Этот метод является единственно верным, если требуется интерактивное взаимодействие с пользователем. Форма откроется уже полностью заполненной на основании переданного документа поступления товаров.
Иногда разработчики сталкиваются с «неидеальным» кодом в старых или сильно кастомизированных конфигурациях, где логика заполнения табличной части жестко «зашита» внутри процедур модуля формы и не продублирована в модуле объекта. Вносить изменения в конфигурацию (например, переносить логику в модуль объекта или добавлять ключевое слово Экспорт к процедуре формы) запрещено регламентом компании или правилами поддержки.
В этом случае у нас остается единственный рабочий вариант — полностью скопировать алгоритм заполнения табличной части из формы документа в модуль нашей внешней обработки и адаптировать его под программное создание объекта.
Давайте проанализируем, как это сделать правильно:
УстановкаЦенНоменклатуры в конфигураторе и найдите процедуру ЗаполнитьТабличнуюЧастьПоПоступлениюНаОсновании.ЭтотОбъект.Товары или просто Товары, замените это обращение на работу через контекст созданного вами объекта, например: ДокОбъект.Товары.Пример адаптированного серверного кода в модуле вашей обработки:
&НаСервере
Процедура ЗаполнитьИЗаписатьДокументВручную(СсылкаДокОснование)
ДокОбъект = Документы.УстановкаЦенНоменклатуры.СоздатьДокумент();
ДокОбъект.Дата = ТекущаяДата();
// Вызываем скопированный и адаптированный алгоритм заполнения
ЗаполнитьТабличнуюЧастьПоПоступлениюЛокально(ДокОбъект, СсылкаДокОснование);
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьТабличнуюЧастьПоПоступлениюЛокально(ДокОбъект, ДокументОснование)
// Логика, скопированная из модуля формы документа.
// Обратите внимание: все действия производятся с переданным параметром ДокОбъект!
ДокОбъект.Товары.Очистить();
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПоступлениеТоваровУслугТовары.Номенклатура КАК Номенклатура,
| ПоступлениеТоваровУслугТовары.Цена КАК Цена
|ИЗ
| Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
|ГДЕ
| ПоступлениеТоваровУслугТовары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ДокументОснование);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
НоваяСтрока = ДокОбъект.Товары.Добавить();
НоваяСтрока.Номенклатура = Выборка.Номенклатура;
НоваяСтрока.Цена = Выборка.Цена;
КонецЦикла;
КонецПроцедуры
Важное предупреждение: Использование решения №3 сопряжено с рисками поддержки. При обновлении типовой конфигурации алгоритмы расчета или структуры метаданных документа могут измениться. Поскольку вы продублировали этот код в своей внешней обработке, вам потребуется вручную актуализировать его после каждого крупного обновления системы.
Мы детально разобрали проблему обращения к процедурам формы из внешнего программного кода. Запомните золотое правило разработки в среде 1С: формы предназначены только для отображения данных пользователю, а вся бизнес-логика должна инкапсулироваться внутри прикладных объектов (модулей объектов и менеджеров). Избегайте прямых вызовов методов управляемых форм снаружи — это убережет ваши решения от трудноуловимых ошибок выполнения и сделает ваш код надежным и масштабируемым.