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