Как изменить сохраненные варианты отчетов кодом, не открывая каждый вручную
Содержание
Чтобы программно обновить вариант отчета в 1С 8.3, нужно получить его настройки СКД (НастройкиКомпоновкиДанных), изменить их через компоновщик и записать обратно в то хранилище, откуда взяли. В типовых конфигурациях на БСП это реквизит Настройки справочника ВариантыОтчетов. В самописных базах это обычно ХранилищеВариантовОтчетов.
Выбор способа зависит от того, что меняете. Пользовательские варианты правят кодом, по одному или пакетом. Предопределенные варианты живут в схеме компоновки отчета, их меняют в конфигураторе или расширением. Пользовательские настройки (то, что человек выставил в форме и не сохранил как вариант) лежат отдельно, и их обычно проще сбросить, чем чинить.
Где 1С хранит варианты отчетов и их настройки
Перед тем как писать код, определите, с каким слоем вы работаете. Их три, и путать их — главная причина «я обновил, а у пользователя ничего не поменялось».
| Что хранится | Где | Как менять |
|---|---|---|
| Предопределенные варианты | Схема СКД отчета, коллекция ВариантыНастроек | Конфигуратор или расширение |
| Пользовательские варианты (БСП) | Справочник ВариантыОтчетов, реквизит Настройки (ХранилищеЗначения) | Код: получить объект, изменить, записать |
| Пользовательские варианты (без БСП) | ХранилищеВариантовОтчетов | Методы Загрузить и Сохранить |
| Пользовательские настройки | ХранилищеПользовательскихНастроекОтчетов | Обычно удаляют, чтобы подтянулся новый вариант |
В свойствах корня конфигурации есть свойство «Хранилище вариантов отчетов». Если оно не заполнено, платформа использует стандартное хранилище системных настроек. В типовых конфигурациях на БСП оно заполнено, и записи фактически попадают в справочник ВариантыОтчетов.
💡 У предопределенного варианта в справочнике БСП тоже есть элемент, но его реквизит Настройки пуст. Настройки при открытии берутся из схемы. Поэтому обновлять такой элемент кодом бессмысленно: правка схемы делается в конфигураторе.
Ключевые реквизиты справочника ВариантыОтчетов
- 🔑
КлючВарианта— строковый ключ, по нему форма отчета ищет вариант. - 📄
Отчет— ссылка на идентификатор объекта метаданных (для встроенных отчетов —ИдентификаторыОбъектовМетаданных). - 👤
Пользовательский—Истинау вариантов, которые создали пользователи. - 💾
Настройки—ХранилищеЗначенияс объектомНастройкиКомпоновкиДанных.
Подготовка: где запускать код и как получить схему отчета
Код обновления вариантов выполняется на сервере. Есть три места, куда его удобно положить:
- Внешняя обработка — для разового исправления. Процедуру помечаете
&НаСервереБезКонтекста, из формы вызываете кнопкой. - Обработчик обновления — если правка нужна после выпуска новой версии вашей доработки. В БСП обработчики регистрируют в модуле обновления подсистемы.
- Расширение конфигурации — если меняете типовую базу и не хотите снимать ее с поддержки.
Права нужны на изменение справочника ВариантыОтчетов. Если обработку запускает не администратор, оберните код в УстановитьПривилегированныйРежим(Истина).
Сделайте копию базы перед массовым обновлением: откатить измененные варианты штатными средствами нельзя.
Получаем схему компоновки
Без схемы компоновщик не сможет проверить, какие поля доступны. Схему берут из макета через менеджер отчета:
ПолноеИмя = "Отчет.ОборотноСальдоваяВедомость";
ИмяОтчета = СтрРазделить(ПолноеИмя, ".")[1];
Схема = Отчеты[ИмяОтчета].ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
Имя макета у разных отчетов разное. Откройте отчет в конфигураторе и посмотрите, какой макет указан в свойстве «Основная схема компоновки данных». Если отчет собирает схему динамически в модуле объекта, проще создать объект отчета и взять ОтчетОбъект.СхемаКомпоновкиДанных.
Алгоритм изменения настроек СКД в варианте отчета
Сам алгоритм из пяти шагов одинаков для любого хранилища.
- Получить
НастройкиКомпоновкиДанныхиз хранилища. - Создать
КомпоновщикНастроекКомпоновкиДанныхи инициализировать его источником на основе схемы. - Загрузить настройки в компоновщик методом
ЗагрузитьНастройки. - Изменить настройки: отбор, выбранные поля, параметры, порядок, структуру.
- Забрать результат из
Компоновщик.Настройкии записать.
Загрузка настроек в компоновщик
Компоновщик = Новый КомпоновщикНастроекКомпоновкиДанных;
Компоновщик.Инициализировать(
Новый ИсточникДоступныхНастроекКомпоновкиДанных(Схема));
Компоновщик.ЗагрузитьНастройки(Настройки);
После инициализации компоновщик знает список доступных полей. Это важно, если вы добавляете в отбор поле, которого в схеме нет: такой элемент останется в настройках, но отчет его проигнорирует или покажет как недоступный.
Типовые правки: отбор, параметр, поле, заголовок
НовыеНастройки = Компоновщик.Настройки;
// Добавить отбор по организации
ЭлементОтбора = НовыеНастройки.Отбор.Элементы.Добавить(
Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Организация");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = Организация;
ЭлементОтбора.Использование = Истина;
// Задать значение параметра данных
НовыеНастройки.ПараметрыДанных.УстановитьЗначениеПараметра(
"Период", Новый СтандартныйПериод(ВариантСтандартногоПериода.ЭтотМесяц));
// Добавить колонку в выбранные поля
ВыбранноеПоле = НовыеНастройки.Выбор.Элементы.Добавить(
Тип("ВыбранноеПолеКомпоновкиДанных"));
ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Количество");
ВыбранноеПоле.Использование = Истина;
// Поменять заголовок отчета
НовыеНастройки.ПараметрыВывода.УстановитьЗначениеПараметра(
"Заголовок", "Остатки по складам");
Если параметра с таким именем в настройках нет, УстановитьЗначениеПараметра ничего не установит. Проверяйте результат через НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Период")): при отсутствии он вернет Неопределено.
Замена переименованного поля
Самая частая задача на практике — в схеме переименовали поле, и старые варианты пользователей сломались. Отбор может содержать группы «И/ИЛИ», поэтому обходить его нужно рекурсивно:
Функция ЗаменитьПолеВОтборе(Элементы, СтароеИмя, НовоеИмя)
Заменено = 0;
Для Каждого Элемент Из Элементы Цикл
Если ТипЗнч(Элемент) = Тип("ГруппаЭлементовОтбораКомпоновкиДанных") Тогда
Заменено = Заменено + ЗаменитьПолеВОтборе(Элемент.Элементы, СтароеИмя, НовоеИмя);
ИначеЕсли Строка(Элемент.ЛевоеЗначение) = СтароеИмя Тогда
Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(НовоеИмя);
Заменено = Заменено + 1;
КонецЕсли;
КонецЦикла;
Возврат Заменено;
КонецФункции
Функция ЗаменитьПолеВВыборе(Элементы, СтароеИмя, НовоеИмя)
Заменено = 0;
Для Каждого Элемент Из Элементы Цикл
Если ТипЗнч(Элемент) = Тип("ГруппаВыбранныхПолейКомпоновкиДанных") Тогда
Заменено = Заменено + ЗаменитьПолеВВыборе(Элемент.Элементы, СтароеИмя, НовоеИмя);
ИначеЕсли ТипЗнч(Элемент) = Тип("ВыбранноеПолеКомпоновкиДанных")
И Строка(Элемент.Поле) = СтароеИмя Тогда
Элемент.Поле = Новый ПолеКомпоновкиДанных(НовоеИмя);
Заменено = Заменено + 1;
КонецЕсли;
КонецЦикла;
Возврат Заменено;
КонецФункции
Проверка типа в выборе нужна потому, что там встречается АвтоВыбранноеПолеКомпоновкиДанных, у которого нет свойства Поле. Тот же прием применяйте к Порядок.Элементы и к отбору и выбору внутри группировок Структура, если вариант их использует.
Функции возвращают число замен. Если оно равно нулю, вариант не трогайте и не записывайте: лишняя запись меняет версию объекта и попадает в обмены.
Сохранение обновленного варианта отчета в хранилище
В конфигурации на БСП
Записывайте через объект справочника, а не через хранилище: так сработают проверки и регистрация изменений.
Объект = СсылкаНаВариант.ПолучитьОбъект();
Объект.Заблокировать();
Объект.Настройки = Новый ХранилищеЗначения(НовыеНастройки);
Объект.Записать();
Заблокировать() ставит объектную блокировку. Если вариант в этот момент открыт у пользователя на редактирование, метод вызовет исключение, и вы обработаете его, а не перезапишете чужую правку.
В конфигурации без БСП
Здесь работают методы стандартного хранилища:
ОписаниеНастроек = Новый ОписаниеНастроек;
Настройки = ХранилищеВариантовОтчетов.Загрузить(
"Отчет.Продажи", КлючВарианта, ОписаниеНастроек, ИмяПользователя);
// ... изменения через компоновщик ...
ХранилищеВариантовОтчетов.Сохранить(
"Отчет.Продажи", КлючВарианта, НовыеНастройки, ОписаниеНастроек, ИмяПользователя);
ОписаниеНастроек передавайте то же, что получили при загрузке. Иначе вариант потеряет представление и в списке появится с ключом вместо названия. Перечень вариантов пользователя возвращает метод ПолучитьСписок.
Если пользователь держит у себя сохраненные пользовательские настройки этого отчета, после обновления варианта он может по-прежнему видеть старый отбор. Пользовательские настройки накладываются поверх варианта. Удалите их через ХранилищеПользовательскихНастроекОтчетов.Удалить(...) для нужного ключа или попросите пользователя в форме отчета вернуть стандартные настройки.
Процедура обновления одного варианта целиком
Все шаги вместе дают одну серверную процедуру для БСП. Она меняет поле Контрагент на Партнер в отборе и выбранных полях и пишет вариант, только если нашлась хотя бы одна замена.
Процедура ОбновитьОдинВариант(СсылкаНаВариант, Схема)
Хранилище = СсылкаНаВариант.Настройки;
Настройки = Хранилище.Получить();
Если Настройки = Неопределено Тогда
Возврат; // предопределенный или пустой вариант
КонецЕсли;
Компоновщик = Новый КомпоновщикНастроекКомпоновкиДанных;
Компоновщик.Инициализировать(
Новый ИсточникДоступныхНастроекКомпоновкиДанных(Схема));
Компоновщик.ЗагрузитьНастройки(Настройки);
НовыеНастройки = Компоновщик.Настройки;
Заменено = ЗаменитьПолеВОтборе(НовыеНастройки.Отбор.Элементы, "Контрагент", "Партнер")
+ ЗаменитьПолеВВыборе(НовыеНастройки.Выбор.Элементы, "Контрагент", "Партнер");
Если Заменено = 0 Тогда
Возврат;
КонецЕсли;
НачатьТранзакцию();
Попытка
Объект = СсылкаНаВариант.ПолучитьОбъект();
Объект.Заблокировать();
Объект.Настройки = Новый ХранилищеЗначения(НовыеНастройки);
Объект.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
ВызватьИсключение без параметров пробрасывает ошибку выше. Там ее ловит внешний цикл, пишет в журнал и идет к следующему варианту. Транзакция здесь короткая: одна запись — одна фиксация.
Ошибки при программном обновлении и их перехват
Каждый вариант обрабатывайте в своей попытке. Один битый вариант не должен останавливать весь проход.
Попытка
ОбновитьОдинВариант(СсылкаНаВариант, Схема);
Обновлено = Обновлено + 1;
Исключение
Текст = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
ЗаписьЖурналаРегистрации("Обновление вариантов отчетов",
УровеньЖурналаРегистрации.Ошибка, , СсылкаНаВариант, Текст);
Ошибок = Ошибок + 1;
КонецПопытки;
ПодробноеПредставлениеОшибки доступна в платформе с номером не ниже 8.3.17. На более ранних платформах берите ОписаниеОшибки().
Отдельный случай — вариант от отчета, которого уже нет в конфигурации. Ссылка в реквизите Отчет ведет на идентификатор удаленного объекта, и Отчеты[ИмяОтчета] упадет. Проверяйте наличие объекта через Метаданные.Отчеты.Найти(ИмяОтчета) до обращения к менеджеру.
Массовое обновление вариантов отчетов: как не положить базу
На базе с сотней пользователей вариантов бывает несколько тысяч. Прямой цикл по справочнику с открытием каждого объекта работает медленно и держит блокировки. Что ускоряет проход:
- 🎯 Выбирайте только нужное запросом. Фильтр по отчету,
Пользовательский = Истина,ПометкаУдаления = Ложь. Объект получайте только для тех, где реально нашлась замена. - 🗂 Кешируйте схему. Одна схема на отчет, сложенная в
Соответствиепо полному имени. Получение макета для каждого варианта заново — лишние обращения к метаданным. - 🔒 Одна транзакция — один вариант. Длинная транзакция на тысячу записей держит блокировки и при ошибке откатывает всё.
- 🕗 Запускайте вне рабочего времени. Тогда меньше конфликтов блокировки.
Запрос, который отбирает пользовательские варианты одного отчета:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВариантыОтчетов.Ссылка КАК Ссылка,
| ВариантыОтчетов.Настройки КАК Настройки
|ИЗ
| Справочник.ВариантыОтчетов КАК ВариантыОтчетов
|ГДЕ
| ВариантыОтчетов.Пользовательский
| И НЕ ВариантыОтчетов.ПометкаУдаления
| И ВЫРАЗИТЬ(ВариантыОтчетов.Отчет КАК Справочник.ИдентификаторыОбъектовМетаданных).ПолноеИмя = &ПолноеИмя";
Запрос.УстановитьПараметр("ПолноеИмя", "Отчет.ОборотноСальдоваяВедомость");
Выборка = Запрос.Выполнить().Выбрать();
Поле Настройки в выборке уже содержит ХранилищеЗначения. Вызывайте Выборка.Настройки.Получить(), проверяйте, нужна ли замена, и только потом открывайте объект для записи. Так база не перезаписывает сотни вариантов, которые менять не нужно.
Как сравнить настройки до и после
Для отладки удобно сериализовать настройки в XML и сравнить строки:
Функция НастройкиВСтроку(Настройки)
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку();
СериализаторXDTO.ЗаписатьXML(Запись, Настройки);
Возврат Запись.Закрыть();
КонецФункции
Если строки до и после совпали, запись пропускаете. Этот же прием помогает выгрузить варианты в файлы перед массовой правкой, чтобы при необходимости восстановить их через СериализаторXDTO.ПрочитатьXML.
Частые вопросы
Можно ли программно изменить предопределенный вариант отчета?
Чем вариант отчета отличается от пользовательских настроек?
Нужно ли вызывать Восстановить у компоновщика?
Компоновщик.Восстановить(СпособВосстановленияНастроекКомпоновкиДанных.ПроверятьДоступность) выбрасывает недоступные поля. Это полезно для чистки, но может удалить поле, которое вы собирались переименовать, поэтому вызывайте его после замены.Как создать новый вариант отчета кодом, а не изменить существующий?
ВариантыОтчетов: заполните Отчет, КлючВарианта, Наименование, Пользовательский = Истина и Настройки. Без БСП достаточно вызвать ХранилищеВариантовОтчетов.Сохранить с новым ключом.Почему после обновления вариант пропал из списка у пользователя?
ОписаниеНастроек передано то же, что было при загрузке.