При работе с элементом управления Планировщик в платформе «1С:Предприятие 8» разработчики часто сталкиваются с непредвиденным поведением системы при заполнении его измерений. Одной из наиболее распространенных является ошибка вида: Ошибка при вызове метода контекста (Добавить) по причине: Недопустимое значение параметра (параметр номер '1'). Возникает она в тот момент, когда мы пытаемся программно добавить элементы в коллекцию измерений планировщика.
В этой статье мы подробно проанализируем причины возникновения данной ошибки, разберем несколько эффективных способов ее решения, а также исправим сопутствующие архитектурные ошибки, которые часто допускаются при написании подобного кода.
Давайте посмотрим на строку кода, которая вызывает падение системы:
НовыйМастер = ИзмерениеМастера.Элементы.Добавить(выб.Мастер);
Метод Добавить() у коллекции элементов измерения планировщика принимает в качестве первого параметра уникальный идентификатор (ключ) элемента измерения. Чаще всего этим ключом выступает ссылка на элемент справочника (например, ссылка на физическое лицо, сотрудника, склад или, как в нашем случае, мастера).
Ключевая особенность коллекции элементов измерения планировщика заключается в том, что каждый элемент в ней должен быть уникальным. Платформа жестко контролирует уникальность ключей. Если мы пытаемся добавить в измерение ссылку, которая уже присутствует в данной коллекции, платформа не проигнорирует это действие, а незамедлительно вызовет исключение: Недопустимое значение параметра (параметр номер '1').
В коде автора темы выборка из запроса обходит документы «Заявка». Если один и тот же мастер указан в нескольких документах за выбранный период времени, то при обходе выборки в цикле ссылка на этого мастера встретится несколько раз. При первой итерации мастер успешно добавится, а на второй — программа аварийно завершит работу.
Самый простой, быстрый и производительный способ избежать ошибки — сделать так, чтобы запрос возвращал только уникальные значения для наших измерений. Для этого достаточно использовать ключевое слово РАЗЛИЧНЫЕ в секции ВЫБРАТЬ вашего запроса.
Давайте проанализируем этот подход по шагам. Мы выбираем только уникальных мастеров, исключая дублирование данных еще на этапе работы СУБД:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| Заявка.Мастер КАК Мастер
|ИЗ
| Документ.Заявка КАК Заявка
|ГДЕ
| Заявка.Дата МЕЖДУ &ДатаНачало И &ДатаКонец
| И Заявка.Мастер <> ЗНАЧЕНИЕ(Справочник.Мастера.ПустаяСсылка)";
Обратите внимание на важное условие в конце запроса. Мы добавили фильтрацию пустых ссылок. Если в документах реквизит «Мастер» окажется незаполненным, пустая ссылка также может вызвать ошибку при добавлении в измерение планировщика. Отсекайте некорректные данные заранее.
Если по каким-то причинам вам необходимо получать полную выборку документов (например, вы параллельно заполняете не только измерения, но и сами события планировщика), то перед добавлением нового элемента в измерение нужно обязательно выполнять проверку на его существование.
Рассмотрим, как правильно реализовать такую проверку в цикле обхода данных:
Пока выб.Следующий() Цикл
// Проверяем, нет ли уже такого мастера в измерениях планировщика
НайденныйЭлемент = ИзмерениеМастера.Элементы.Найти(выб.Мастер);
Если НайденныйЭлемент = Неопределено Тогда
// Элемент не найден, можем смело добавлять
НовыйМастер = ИзмерениеМастера.Элементы.Добавить(выб.Мастер);
НовыйМастер.Текст = выб.Мастер.Наименование;
КонецЕсли;
КонецЦикла;
Этот способ гарантирует безопасность выполнения кода: даже если в выборке окажется тысяча дубликатов, система обработает их корректно и без ошибок.
Если данные для планировщика подготавливаются во временных структурах, то можно использовать промежуточную выгрузку в таблицу значений. Этот метод как раз помог автору темы на начальном этапе.
Разберем, как выглядит этот алгоритм:
ТЗ = Запрос.Выполнить().Выгрузить();
// Оставляем в таблице только уникальные строки по колонке "Мастер"
ТЗ.Свернуть("Мастер");
Для Каждого Стр Из ТЗ Цикл
// Исключаем пустые значения, которые могли оказаться в таблице
Если ЗначениеЗаполнено(Стр.Мастер) Тогда
НовыйМастер = ИзмерениеМастера.Элементы.Добавить(Стр.Мастер);
НовыйМастер.Текст = Строка(Стр.Мастер);
КонецЕсли;
КонецЦикла;
Хотя метод со сверткой таблицы значений рабочий, он требует выделения дополнительной оперативной памяти под объект ТаблицаЗначений, поэтому для больших объемов данных предпочтительнее использовать Способ 1.
Проанализируем исходный код из сообщения автора темы. Процедура Отобразить(Команда) объявлена с директивой компиляции &НаКлиенте. При этом внутри процедуры создается объект Новый Запрос.
Важно помнить: В управляемом приложении 1С объект Запрос (а также сопутствующие объекты ВыборкаИзРезультатаЗапроса и ТаблицаЗначений) доступны исключительно на сервере. Попытка запустить такой код в режиме тонкого клиента вызовет ошибку компиляции. В толстом клиенте этот код может запуститься, но это является крайне неоптимальной и устаревшей практикой разработки.
Для исправления этой архитектурной ошибки нам необходимо разделить логику на клиентскую и серверную части. Планировщик является реквизитом формы, поэтому его коллекции (включая измерения) можно без проблем заполнять на сервере.
Давайте перепишем код правильно:
&НаКлиенте
Процедура Отобразить(Команда)
// На клиенте выполняем только интерактивные действия, если они нужны,
// и передаем управление на сервер
ОтобразитьНаСервере();
КонецПроцедуры
&НаСервере
Процедура ОтобразитьНаСервере()
// Настройка периодов планировщика
Планировщик.ТекущиеПериодыОтображения.Очистить();
Планировщик.ТекущиеПериодыОтображения.Добавить(НачалоДня(ТекущаяДата()), КонецДня(ТекущаяДата()));
Планировщик.ЕдиницаПериодическогоВарианта = ТипЕдиницыШкалыВремени.Час;
Планировщик.КратностьПериодическогоВарианта = 24;
Планировщик.ОтображатьПеренесенныеЗаголовки = Истина;
Планировщик.ОтступСНачалаПереносаШкалыВремени = 8;
Планировщик.ОтступСКонцаПереносаШкалыВремени = 3;
// Инициализируем измерения планировщика
ИзмеренияПланировщика = Планировщик.Измерения;
ИзмеренияПланировщика.Очистить();
ИзмерениеМастера = ИзмеренияПланировщика.Добавить("Мастера");
// Создаем и выполняем запрос на сервере
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| Заявка.Мастер КАК Мастер
|ИЗ
| Документ.Заявка КАК Заявка
|ГДЕ
| Заявка.Дата МЕЖДУ &ДатаНачало И &ДатаКонец
| И Заявка.Мастер <> ЗНАЧЕНИЕ(Справочник.Мастера.ПустаяСсылка)";
Запрос.УстановитьПараметр("ДатаНачало", НачалоГода(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонец", КонецДня(ТекущаяДата()));
Выборка = Запрос.Perform().Выбрать();
Пока Выборка.Следующий() Цикл
НовыйМастер = ИзмерениеМастера.Элементы.Добавить(Выборка.Мастер);
НовыйМастер.Текст = Выборка.Мастер.Наименование;
КонецЦикла;
КонецПроцедуры
При таком подходе код работает максимально быстро, не перегружает сетевой трафик между клиентом и сервером и полностью соответствует стандартам разработки фирмы «1С».
Раз уж мы заговорили о заполнении измерений планировщика, рассмотрим современную возможность платформы по кастомизации внешнего вида заголовков. Начиная с версии платформы 8.3.18, свойству Текст элемента измерения планировщика можно присваивать не только простую строку, но и ФорматированнуюСтроку.
Это позволяет делать заголовки интерактивными и визуально привлекательными. Например, вы можете выделить должность мастера серым цветом, а его фамилию сделать жирной:
// Пример создания форматированного заголовка для элемента измерения
МассивЭлементовСтроки = Новый Массив;
МассивЭлементовСтроки.Добавить(Новый ФорматированнаяСтрока("Мастер: ", Новый Шрифт(,,Истина))); // Курсив
МассивЭлементовСтроки.Добавить(Новый ФорматированнаяСтрока(Выборка.Мастер.Наименование, Новый Шрифт(,,,,,Истина))); // Жирный
НовыйМастер.Текст = Новый ФорматированнаяСтрока(МассивЭлементовСтроки);
Используйте этот прием, чтобы сделать ваши интерфейсы более интуитивными и приятными для конечных пользователей.