Как программно заполнить документ, если логика расчета находится в модуле формы

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

При разработке внешних обработок для массового создания или изменения документов программисты часто сталкиваются с ситуацией, когда ключевые расчеты (например, заполнение счетов учета, расчет налогов или распределение платежей) «зашиты» в модуле формы документа. Типовые процедуры вроде ВидОперацииПриИзменении часто принимают в качестве параметра ЭтотОбъект, который в контексте формы является типом УправляемаяФорма. Попытка вызвать такой код из серверной обработки, передавая туда ДокументОбъект, неизбежно приводит к ошибке несоответствия типов.

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

Почему нельзя просто «получить форму» на сервере

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

  1. Производительность: Создание объекта формы — это крайне ресурсоемкая операция. Если вам нужно создать 1000 документов «Поступление на расчетный счет», инициализация тысячи форм приведет к колоссальным затратам памяти и процессорного времени сервера.
  2. Интерактивность: Многие методы в модулях форм рассчитаны на взаимодействие с пользователем (открытие окон, вопросы, работа с элементами управления Элементы). На сервере этого контекста не существует.
  3. Типизация: Процедуры в типовых конфигурациях (например, в БП 3.0) часто обращаются к свойствам ЭтаФорма.Элементы.ТоварыСчетаУчета.Видимость. Если вы передадите в такую процедуру обычный ДокументОбъект, система выдаст ошибку, так как у объекта нет коллекции Элементы.

Способ №1: Рефакторинг и вынос логики в свою процедуру

Рассмотрим самый надежный способ. Если мы видим, что процедура ПоступлениеНаРасчетныйСчетФормы.ВидОперацииПриИзмененииСервер(ЭтотОбъект) требует форму, нам необходимо проанализировать, что именно она делает. Чаще всего она просто пересчитывает данные в коллекции Объект.РасшифровкаПлатежа.

Разберем по шагам, как адаптировать этот код для внешней обработки:

  1. Откройте общий модуль ПоступлениеНаРасчетныйСчетФормы.
  2. Найдите процедуру ВидОперацииПриИзмененииСервер.
  3. Скопируйте алгоритм в свою обработку.
  4. Замените все обращения к элементам формы на обращения к реквизитам вашего объекта.

Пример адаптации кода:


// Было в типовой форме:
Процедура ВидОперацииПриИзмененииСервер(Форма) Экспорт
    ОбъектФормы = Форма.Объект;
    // ... работа с Форма.Элементы ...
    ЗаполнитьСчетаУчета(ОбъектФормы);
КонецПроцедуры

// Стало в вашей обработке:
Процедура МойЗаполнитьВидОперации(НовыйДокумент)
    // Мы работаем напрямую с ДокументОбъект
    НовыйДокумент.ВидОперации = ПредопределенноеЗначение("Перечисление.ВидыОперацийПоступлениеДенежныхСредств.ОплатаПокупателя");
    
    // Вместо вызова процедур формы вызываем процедуры общего модуля, 
    // которые умеют работать с объектом, а не с формой.
    // В БП 3.0 это часто модули с суффиксом "Перезаполнение" или "Сервер"
    БухгалтерскийУчетПерезаполнение.ЗаполнитьСтатьюДДС(НовыйДокумент);
КонецПроцедуры

Способ №2: Использование механизма «ОбработкаЗаполнения»

Проанализируем архитектуру современных конфигураций на базе БСП. Основная часть логики заполнения документа при программном создании должна находиться в модуле объекта в процедуре ОбработкаЗаполнения.

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


НовыйДок = Документы.ПоступлениеНаРасчетныйСчет.СоздатьОбъект();

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

// Вызов этого метода инициирует событие ОбработкаЗаполнения в модуле документа
// Там обычно уже реализован вызов всех серверных процедур по умолчанию
НовыйДок.Заполнить(ДанныеЗаполнения);

// Если какие-то реквизиты не заполнились, дозаполняем их вручную
НовыйДок.СуммаДокумента = 1000;
НовыйДок.Записать();

Способ №3: Поиск альтернативных методов в API конфигурации

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

Например, для документа «Поступление на расчетный счет» в 1С:Бухгалтерии 3.0 стоит обратить внимание на следующие модули:

Вместо вызова ПоступлениеНаРасчетныйСчетФормыКлиентСервер.ЗаполнитьРеквизитыРасшифровкаПлатежа(ЭтотОбъект, Истина), проанализируйте содержимое этой процедуры. Скорее всего, внутри она вызывает другую функцию, которая делает расчеты. Именно эту «глубинную» функцию и нужно использовать в вашей обработке.

Посмотрим на пример вызова расчета счетов учета без формы:


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

// Метод, который работает с данными, а не с экранными формами
СчетаУчетаРасчетов.Заполнить(Объект, ПараметрыЗаполнения);

Как «обмануть» систему с помощью структуры (Advanced)

Если код в общем модуле написан не совсем удачно и обращается к реквизиту Форма.Объект, иногда помогает передача «фейкового» объекта. Мы можем создать структуру, которая имитирует объект формы. Рассмотрим этот прием:

Если процедура ожидает параметр Форма и обращается к ней как Форма.Объект.Контрагент, мы можем попробовать следующее:


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

// Если процедура не обращается к свойству "Элементы", она "проглотит" эту структуру
ПоступлениеНаРасчетныйСчетФормы.ВидОперацииПриИзмененииСервер(КонтекстИмитации);

Важно: Этот метод сработает только в том случае, если в вызываемой процедуре нет обращений к элементам оформления, кнопкам или другим чисто интерфейсным свойствам. Если такие обращения есть, код упадет с ошибкой «Поле объекта не обнаружено (Элементы)».

Рекомендации по реализации массового создания документов

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

  1. Никогда не используйте ПолучитьФорму() на сервере для заполнения данных. Это архитектурная ошибка.
  2. Изучайте модуль менеджера документа. В современных конфигурациях логика часто выносится в МодульМенеджера.ПодготовитьДанныеДляЗаполнения().
  3. Копируйте код, если он недоступен. Если нужный расчет жестко привязан к форме и не вынесен в API, лучше аккуратно скопировать этот кусок кода в свою обработку и адаптировать его под работу с ДокументОбъект. Это обеспечит независимость вашей обработки от изменений интерфейса документа в будущих обновлениях.
  4. Используйте ДанныеФормыВЗначение и ЗначениеВДанныеФормы. Если вы все же работаете в контексте формы (например, в команде формы), используйте эти методы для преобразования ДанныеФормыСтруктура в полноценный ДокументОбъект, выполняйте над ним действия и возвращайте обратно.

Таким образом, решение задачи кроется не в поиске способа «протащить» форму на сервер, а в грамотном выделении бизнес-логики из кода интерфейса.

← На главную