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