При разработке обработок загрузки данных из внешних источников (например, из файлов Microsoft Excel) разработчики периодически сталкиваются с ошибкой выполнения: «Данная команда не может выполняться в формуле элемента диалога!». Ошибка обычно возникает в момент, когда обработка открывает форму нового элемента справочника и пытается программно вызвать метод Записать() внутри предопределенной процедуры ПриОткрытии().
В этой статье мы подробно разберем, почему платформа 1С:Предприятие 7.7 блокирует вызов этого метода, почему аналогичный код для документов может работать, и как правильно реализовать создание элементов справочников и подчиненных объектов без лишней интерактивности.
Проанализируем механику работы экранных форм в 1С 7.7, чтобы понять источник проблемы:
ПриОткрытии() или ВводНового() визуальный диалог элемента еще не построен до конца. Платформа находится в фазе инициализации интерфейса и жестко блокирует вызовы методов фиксации данных диалога, таких как Записать(). Любая попытка сохранить форму на этом этапе трактуется платформой как некорректная команда, выполняемая в контексте формулы диалога.ПриЗаписи() и ПриЗакрытии() напрямую из кода не запускает системные механизмы транзакции или штатного закрытия окна. Это просто исполнение пользовательских процедур с такими именами. Метод Записать() при этом пытается принудительно перевести форму в сохраненное состояние, что недопустимо.ЗаписатьВФорме(), а также специфическая логика работы транзакций при открытии. У справочников метод ЗаписатьВФорме() отсутствует, а интерактивная запись жестко привязана к штатным системным кнопкам диалога.Открытие визуальной формы справочника (тем более модально) ради того, чтобы заполнились стандартные реквизиты или создались единицы измерения — неоптимальный подход. Он существенно замедляет импорт, расходует стек графической подсистемы и приводит к конфликтам контекста. Рассмотрим, как решить эту задачу правильно с помощью чистого программного кода в самой обработке.
Разберем по шагам алгоритм создания позиции номенклатуры и подчиненной базовой единицы измерения:
Справочник.Номенклатура.ИспользоватьВладельца().Посмотрим на пример реализации данного алгоритма:
// Создаем объект номенклатуры
СпрН = СоздатьОбъект("Справочник.Номенклатура");
СпрН.Новый();
СпрН.Наименование = СокрЛП(ОкноExcel.Cells(Сч, 3).Value);
СпрН.ПолнНаименование = СпрН.Наименование;
СпрН.Артикул = СокрЛП(ОкноExcel.Cells(Сч, 2).Value);
// Первичная запись для получения ссылки в базе данных
СпрН.Записать();
// Создаем подчиненную единицу измерения
СпрЕд = СоздатьОбъект("Справочник.Единицы");
СпрЕд.ИспользоватьВладельца(СпрН.ТекущийЭлемент());
СпрЕд.Новый();
// Заполняем реквизиты единицы (например, штуки)
СпрЕд.Наименование = "шт";
СпрЕд.Коэффициент = 1;
// Если в конфигурации используется классификатор ОКЕИ:
// СпрЕд.ОКЕИ = Константа.БазоваяЕдиницаШтука;
СпрЕд.Записать();
// Привязываем созданную единицу к реквизитам товара
СпрН.БазоваяЕдиница = СпрЕд.ТекущийЭлемент();
СпрН.ОсновнаяЕдиница = СпрЕд.ТекущийЭлемент();
СпрН.Записать();
// Сохраняем ссылку на созданный товар для дальнейшего использования
глИмпортируемыйТовар = СпрН.ТекущийЭлемент();
Такой подход полностью устраняет зависимость от интерфейсных событий, выполняется значительно быстрее и исключает возникновение ошибок формул диалога.
Если в конфигурации в процедуре ПриЗаписи() формы номенклатуры реализована обширная и сложная бизнес-логика, перенос которой в обработку нецелесообразен, можно использовать альтернативный сценарий с сохранением через процедуру ПриЗакрытии().
Рассмотрим, как обойти ограничение фазы инициализации формы:
ПриОткрытии() мы только считываем параметры, заполняем реквизиты диалога, выставляем специальный флаг (переменную модуля) и сразу отдаем команду на закрытие формы: Форма.Закрыть(0);.ПриЗакрытии() проверяем выставленный флаг. В этот момент форма уже вышла из стадии ПриОткрытии(), поэтому вызов Записать() выполняется без системного конфликта.Посмотрим на пример структуры модуля формы справочника:
Перем мАвтоЗапись;
Процедура ПриОткрытии()
Если ПустоеЗначение(Форма.Параметр) = 0 Тогда
ПолучПараметр = Форма.Параметр;
ОкноExcel = ПолучПараметр.Получить("ОкноExcel");
Сч = ПолучПараметр.Получить("Сч");
Наименование = ОкноExcel.Cells(Сч, 3).Value;
ПолнНаименование = Наименование;
Артикул = ОкноExcel.Cells(Сч, 2).Value;
// Взводим флаг для отложенной записи
мАвтоЗапись = 1;
// Закрываем окно, не дожидаясь отрисовки
Форма.Закрыть(0);
КонецЕсли;
КонецПроцедуры
Процедура ПриЗакрытии()
Если мАвтоЗапись = 1 Тогда
мАвтоЗапись = 0;
Записать();
глИмпортируемыйТовар = ТекущийЭлемент();
КонецЕсли;
КонецПроцедуры
Также для эмуляции штатного интерактивного сохранения можно использовать стандартную формулу кнопки с кодом #Закрыть (аналог кнопки «ОК» с сохранением). При выполнении этой команды платформа сама корректно инициирует проверку заполнения, отрабатывает событие ПриЗаписи() и закрывает окно.
Если в системе установлены внешние компоненты (например, FormEx), можно использовать событие ПослеОткрытия() или метод отложенного вызова процедур: в этом случае сохранение произойдет в тот момент, когда форма уже полностью открыта, и ошибка также не возникнет.