При работе с типовыми конфигурациями на платформе 1С:Предприятие 8.3, особенно с такими сложными продуктами, как «Зарплата и управление персоналом», редакция 3.1, разработчики иногда сталкиваются с ситуациями, когда система ведет себя некорректно в нештатных режимах запуска. Одной из таких проблем является ошибка «Итератор для значения не определен» при выполнении кода в модулях, связанных с электронным документооборотом с контролирующими органами.
В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем технические причины несовместимости режимов работы платформы и рассмотрим способы решения проблемы, включая корректную модификацию кода и административные настройки.
Рассмотрим ситуацию: при запуске обработки «Документооборот с контролирующими органами» в конфигурации ЗУП 3.1 (например, релиз 3.1.9.159) система аварийно завершает работу со следующим сообщением:
{Обработка.ДокументооборотСКонтролирующимиОрганами.Форма.КонтейнерКлиентскихМетодов.Форма(32125)}: Итератор для значения не определен
Для Каждого ОткрытоеОкно Из ПолучитьОкна() Цикл
Проанализируем этот фрагмент кода. Ошибка «Итератор для значения не определен» в контексте цикла Для Каждого означает, что функция, стоящая после ключевого слова Из, вернула значение Неопределено или пустое значение, которое не является коллекцией, пригодной для обхода. В данном случае виновником является встроенная функция платформы ПолучитьОкна().
Важный технический нюанс: функция ПолучитьОкна() предназначена для работы исключительно в режиме управляемого приложения. Она возвращает коллекцию окон клиентского приложения. Если же база по каким-то причинам была запущена в режиме «Толстый клиент (обычное приложение)», эта функция не работает так, как ожидает типовой код ЗУП 3.1, поскольку сама концепция окон в обычном приложении реализована иначе.
Прежде чем исправлять код, нам необходимо понять, как современная конфигурация, полностью построенная на управляемых формах, оказалась в режиме обычного приложения. Это может произойти в следующих случаях:
/RunModeOrdinaryApplication.Наиболее правильным решением «без программирования» будет принудительный перевод пользователя в режим Тонкого клиента или Управляемого приложения через настройки базы в списке стартера 1С или в конфигураторе.
Если по каким-то причинам избежать запуска в обычном приложении невозможно, нам придется внести изменения в код. Рассмотрим пример модификации функции ФормаРасшифровкиСообщенийОткрыта(), предложенный в сообществе.
Нам необходимо добавить проверку режима запуска в самом начале функции, чтобы в случае обычного приложения выполнение кода прерывалось до вызова проблемной функции ПолучитьОкна(). Для этого мы будем использовать инструкции препроцессора.
Проанализируем предлагаемый код:
&НаКлиенте
Функция ФормаРасшифровкиСообщенийОткрыта()
// Вставка кода для предотвращения ошибки в обычном приложении
#Если ТолстыйКлиентОбычноеПриложение Тогда
Возврат Ложь;
#КонецЕсли
ИмяФормыСообщенийТребующихРасшифровки = ПутьКОбъекту + ".Форма.ПоказСообщенийТребующихРасшифровки";
ИмяФормыОшибкиОтправки = ПутьКОбъекту + ".Форма.ОшибкиОтправки";
ИмяФормыРезультатОбмена = ПутьКОбъекту + ".Форма.РезультатОбмена";
ИмяФормыДлительноеДействие = ПутьКОбъекту + ".Форма.ДлительноеДействие";
// Для формы, открытой в режиме "РежимОткрытияОкнаФормы.БлокироватьОкноВладельца",
// не выполняется поиск по уникальному идентификатору, поэтому приходится искать самим.
Для Каждого ОткрытоеОкно Из ПолучитьОкна() Цикл
Если ТипЗнч(ОткрытоеОкно) = Тип("ОкноКлиентскогоПриложения")
И ОткрытоеОкно.Содержимое.Количество() = 1
И (ОткрытоеОкно.Содержимое[0].ИмяФормы = ИмяФормыСообщенийТребующихРасшифровки
ИЛИ ОткрытоеОкно.Содержимое[0].ИмяФормы = ИмяФормыОшибкиОтправки
ИЛИ ОткрытоеОкно.Содержимое[0].ИмяФормы = ИмяФормыРезультатОбмена
ИЛИ ОткрытоеОкно.Содержимое[0].ИмяФормы = ИмяФормыДлительноеДействие) Тогда
Возврат ОткрытоеОкно.Содержимое[0].Открыта();
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
В данном примере использование директивы #Если ТолстыйКлиентОбычноеПриложение Тогда гарантирует, что при запуске в «неправильном» режиме функция просто вернет Ложь (как будто окна не найдены), не доходя до строки с итератором. Это безопасный способ, который не затронет работу пользователей в управляемом интерфейсе (Тонкий клиент или Веб-клиент).
Для того чтобы не снимать типовую конфигурацию с поддержки и выполнить требование «не вносить изменения в типовой код», лучше всего использовать механизм Расширений конфигурации. Разберем по шагам, как это сделать:
ДокументооборотСКонтролирующимиОрганами и её форму КонтейнерКлиентскихМетодов.ФормаРасшифровкиСообщенийОткрыта.&Вместо для полной замены логики функции.Пример кода в расширении будет выглядеть так:
&Вместо("ФормаРасшифровкиСообщенийОткрыта")
Функция Расш1_ФормаРасшифровкиСообщенийОткрыта()
#Если ТолстыйКлиентОбычноеПриложение Тогда
// В обычном приложении функция ПолучитьОкна() недоступна,
// поэтому считаем, что форма не открыта.
Возврат Ложь;
#Иначе
// Вызываем логику стандартной функции или копируем её код
// для работы в управляемом режиме.
Возврат ПродолжитьВызов();
#КонецЕсли
КонецФункции
Использование метода ПродолжитьВызов() внутри расширения позволяет выполнить оригинальный код конфигурации, если мы находимся в управляемом приложении, и полностью избежать его выполнения в обычном приложении. Это наиболее архитектурно верный подход в системе 1С:Предприятие 8.3.
В ходе обсуждения проблемы часто звучит предложение использовать функцию Вычислить("ПолучитьОкна()"). Разберем, поможет ли это нам. Функция Вычислить() позволяет выполнить строку кода во время работы программы. С точки зрения синтаксической проверки в Конфигураторе, такой код действительно «пройдет» проверку, так как компилятор не знает, что будет внутри строки.
Однако проблему это не решит. В момент исполнения кода в обычном приложении, система попытается выполнить текст «ПолучитьОкна()», обнаружит, что такой функции в данном контексте не существует, и выдаст ошибку времени выполнения. Поэтому использование Вычислить() — это лишь способ «обмануть» синтакс-проверку, но не реальное решение технической несовместимости.
Подведем итоги нашего анализа. Ошибка в модуле документооборота ЗУП 3.1 при запуске в обычном приложении является следствием того, что типовое решение ориентировано строго на современные стандарты интерфейса 1С.
Рекомендуемый порядок действий:
&Вместо или &Перед добавить проверку на тип клиента с помощью инструкций препроцессора #Если.Вычислить(), так как они лишь маскируют проблему, но не устраняют её причину, что приведет к ошибкам на следующих этапах работы с 1С-Отчетностью.Помните, что стабильная работа 1С-Отчетности и механизмов криптографии в 1С гарантируется производителем только в управляемом режиме (Тонкий/Толстый клиент управляемое приложение).