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