Как изменить сохраненные варианты отчетов кодом, не открывая каждый вручную

· 9 мин

Содержание

Чтобы программно обновить вариант отчета в 1С 8.3, нужно получить его настройки СКД (НастройкиКомпоновкиДанных), изменить их через компоновщик и записать обратно в то хранилище, откуда взяли. В типовых конфигурациях на БСП это реквизит Настройки справочника ВариантыОтчетов. В самописных базах это обычно ХранилищеВариантовОтчетов.

Выбор способа зависит от того, что меняете. Пользовательские варианты правят кодом, по одному или пакетом. Предопределенные варианты живут в схеме компоновки отчета, их меняют в конфигураторе или расширением. Пользовательские настройки (то, что человек выставил в форме и не сохранил как вариант) лежат отдельно, и их обычно проще сбросить, чем чинить.

Где 1С хранит варианты отчетов и их настройки

Перед тем как писать код, определите, с каким слоем вы работаете. Их три, и путать их — главная причина «я обновил, а у пользователя ничего не поменялось».

Что хранитсяГдеКак менять
Предопределенные вариантыСхема СКД отчета, коллекция ВариантыНастроекКонфигуратор или расширение
Пользовательские варианты (БСП)Справочник ВариантыОтчетов, реквизит Настройки (ХранилищеЗначения)Код: получить объект, изменить, записать
Пользовательские варианты (без БСП)ХранилищеВариантовОтчетовМетоды Загрузить и Сохранить
Пользовательские настройкиХранилищеПользовательскихНастроекОтчетовОбычно удаляют, чтобы подтянулся новый вариант

В свойствах корня конфигурации есть свойство «Хранилище вариантов отчетов». Если оно не заполнено, платформа использует стандартное хранилище системных настроек. В типовых конфигурациях на БСП оно заполнено, и записи фактически попадают в справочник ВариантыОтчетов.

💡 У предопределенного варианта в справочнике БСП тоже есть элемент, но его реквизит Настройки пуст. Настройки при открытии берутся из схемы. Поэтому обновлять такой элемент кодом бессмысленно: правка схемы делается в конфигураторе.

Ключевые реквизиты справочника ВариантыОтчетов

  • 🔑 КлючВарианта — строковый ключ, по нему форма отчета ищет вариант.
  • 📄 Отчет — ссылка на идентификатор объекта метаданных (для встроенных отчетов — ИдентификаторыОбъектовМетаданных).
  • 👤 Пользовательский — Истина у вариантов, которые создали пользователи.
  • 💾 Настройки — ХранилищеЗначения с объектом НастройкиКомпоновкиДанных.

Подготовка: где запускать код и как получить схему отчета

Код обновления вариантов выполняется на сервере. Есть три места, куда его удобно положить:

  1. Внешняя обработка — для разового исправления. Процедуру помечаете &НаСервереБезКонтекста, из формы вызываете кнопкой.
  2. Обработчик обновления — если правка нужна после выпуска новой версии вашей доработки. В БСП обработчики регистрируют в модуле обновления подсистемы.
  3. Расширение конфигурации — если меняете типовую базу и не хотите снимать ее с поддержки.

Права нужны на изменение справочника ВариантыОтчетов. Если обработку запускает не администратор, оберните код в УстановитьПривилегированныйРежим(Истина).

Сделайте копию базы перед массовым обновлением: откатить измененные варианты штатными средствами нельзя.

Получаем схему компоновки

Без схемы компоновщик не сможет проверить, какие поля доступны. Схему берут из макета через менеджер отчета:

ПолноеИмя = "Отчет.ОборотноСальдоваяВедомость";
ИмяОтчета = СтрРазделить(ПолноеИмя, ".")[1];
Схема = Отчеты[ИмяОтчета].ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");

Имя макета у разных отчетов разное. Откройте отчет в конфигураторе и посмотрите, какой макет указан в свойстве «Основная схема компоновки данных». Если отчет собирает схему динамически в модуле объекта, проще создать объект отчета и взять ОтчетОбъект.СхемаКомпоновкиДанных.

Алгоритм изменения настроек СКД в варианте отчета

Сам алгоритм из пяти шагов одинаков для любого хранилища.

  1. Получить НастройкиКомпоновкиДанных из хранилища.
  2. Создать КомпоновщикНастроекКомпоновкиДанных и инициализировать его источником на основе схемы.
  3. Загрузить настройки в компоновщик методом ЗагрузитьНастройки.
  4. Изменить настройки: отбор, выбранные поля, параметры, порядок, структуру.
  5. Забрать результат из Компоновщик.Настройки и записать.

Загрузка настроек в компоновщик

Компоновщик = Новый КомпоновщикНастроекКомпоновкиДанных;
Компоновщик.Инициализировать(
    Новый ИсточникДоступныхНастроекКомпоновкиДанных(Схема));
Компоновщик.ЗагрузитьНастройки(Настройки);

После инициализации компоновщик знает список доступных полей. Это важно, если вы добавляете в отбор поле, которого в схеме нет: такой элемент останется в настройках, но отчет его проигнорирует или покажет как недоступный.

Типовые правки: отбор, параметр, поле, заголовок

НовыеНастройки = Компоновщик.Настройки;

// Добавить отбор по организации
ЭлементОтбора = НовыеНастройки.Отбор.Элементы.Добавить(
    Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение  = Новый ПолеКомпоновкиДанных("Организация");
ЭлементОтбора.ВидСравнения   = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = Организация;
ЭлементОтбора.Использование  = Истина;

// Задать значение параметра данных
НовыеНастройки.ПараметрыДанных.УстановитьЗначениеПараметра(
    "Период", Новый СтандартныйПериод(ВариантСтандартногоПериода.ЭтотМесяц));

// Добавить колонку в выбранные поля
ВыбранноеПоле = НовыеНастройки.Выбор.Элементы.Добавить(
    Тип("ВыбранноеПолеКомпоновкиДанных"));
ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Количество");
ВыбранноеПоле.Использование = Истина;

// Поменять заголовок отчета
НовыеНастройки.ПараметрыВывода.УстановитьЗначениеПараметра(
    "Заголовок", "Остатки по складам");

Если параметра с таким именем в настройках нет, УстановитьЗначениеПараметра ничего не установит. Проверяйте результат через НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Период")): при отсутствии он вернет Неопределено.

Замена переименованного поля

Самая частая задача на практике — в схеме переименовали поле, и старые варианты пользователей сломались. Отбор может содержать группы «И/ИЛИ», поэтому обходить его нужно рекурсивно:

Функция ЗаменитьПолеВОтборе(Элементы, СтароеИмя, НовоеИмя)
    Заменено = 0;
    Для Каждого Элемент Из Элементы Цикл
        Если ТипЗнч(Элемент) = Тип("ГруппаЭлементовОтбораКомпоновкиДанных") Тогда
            Заменено = Заменено + ЗаменитьПолеВОтборе(Элемент.Элементы, СтароеИмя, НовоеИмя);
        ИначеЕсли Строка(Элемент.ЛевоеЗначение) = СтароеИмя Тогда
            Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(НовоеИмя);
            Заменено = Заменено + 1;
        КонецЕсли;
    КонецЦикла;
    Возврат Заменено;
КонецФункции

Функция ЗаменитьПолеВВыборе(Элементы, СтароеИмя, НовоеИмя)
    Заменено = 0;
    Для Каждого Элемент Из Элементы Цикл
        Если ТипЗнч(Элемент) = Тип("ГруппаВыбранныхПолейКомпоновкиДанных") Тогда
            Заменено = Заменено + ЗаменитьПолеВВыборе(Элемент.Элементы, СтароеИмя, НовоеИмя);
        ИначеЕсли ТипЗнч(Элемент) = Тип("ВыбранноеПолеКомпоновкиДанных")
            И Строка(Элемент.Поле) = СтароеИмя Тогда
            Элемент.Поле = Новый ПолеКомпоновкиДанных(НовоеИмя);
            Заменено = Заменено + 1;
        КонецЕсли;
    КонецЦикла;
    Возврат Заменено;
КонецФункции

Проверка типа в выборе нужна потому, что там встречается АвтоВыбранноеПолеКомпоновкиДанных, у которого нет свойства Поле. Тот же прием применяйте к Порядок.Элементы и к отбору и выбору внутри группировок Структура, если вариант их использует.

Функции возвращают число замен. Если оно равно нулю, вариант не трогайте и не записывайте: лишняя запись меняет версию объекта и попадает в обмены.

Сохранение обновленного варианта отчета в хранилище

В конфигурации на БСП

Записывайте через объект справочника, а не через хранилище: так сработают проверки и регистрация изменений.

Объект = СсылкаНаВариант.ПолучитьОбъект();
Объект.Заблокировать();
Объект.Настройки = Новый ХранилищеЗначения(НовыеНастройки);
Объект.Записать();

Заблокировать() ставит объектную блокировку. Если вариант в этот момент открыт у пользователя на редактирование, метод вызовет исключение, и вы обработаете его, а не перезапишете чужую правку.

В конфигурации без БСП

Здесь работают методы стандартного хранилища:

ОписаниеНастроек = Новый ОписаниеНастроек;
Настройки = ХранилищеВариантовОтчетов.Загрузить(
    "Отчет.Продажи", КлючВарианта, ОписаниеНастроек, ИмяПользователя);

// ... изменения через компоновщик ...

ХранилищеВариантовОтчетов.Сохранить(
    "Отчет.Продажи", КлючВарианта, НовыеНастройки, ОписаниеНастроек, ИмяПользователя);

ОписаниеНастроек передавайте то же, что получили при загрузке. Иначе вариант потеряет представление и в списке появится с ключом вместо названия. Перечень вариантов пользователя возвращает метод ПолучитьСписок.

Если пользователь держит у себя сохраненные пользовательские настройки этого отчета, после обновления варианта он может по-прежнему видеть старый отбор. Пользовательские настройки накладываются поверх варианта. Удалите их через ХранилищеПользовательскихНастроекОтчетов.Удалить(...) для нужного ключа или попросите пользователя в форме отчета вернуть стандартные настройки.

Процедура обновления одного варианта целиком

Все шаги вместе дают одну серверную процедуру для БСП. Она меняет поле Контрагент на Партнер в отборе и выбранных полях и пишет вариант, только если нашлась хотя бы одна замена.

Процедура ОбновитьОдинВариант(СсылкаНаВариант, Схема)
    Хранилище = СсылкаНаВариант.Настройки;
    Настройки = Хранилище.Получить();
    Если Настройки = Неопределено Тогда
        Возврат; // предопределенный или пустой вариант
    КонецЕсли;

    Компоновщик = Новый КомпоновщикНастроекКомпоновкиДанных;
    Компоновщик.Инициализировать(
        Новый ИсточникДоступныхНастроекКомпоновкиДанных(Схема));
    Компоновщик.ЗагрузитьНастройки(Настройки);
    НовыеНастройки = Компоновщик.Настройки;

    Заменено = ЗаменитьПолеВОтборе(НовыеНастройки.Отбор.Элементы, "Контрагент", "Партнер")
        + ЗаменитьПолеВВыборе(НовыеНастройки.Выбор.Элементы, "Контрагент", "Партнер");
    Если Заменено = 0 Тогда
        Возврат;
    КонецЕсли;

    НачатьТранзакцию();
    Попытка
        Объект = СсылкаНаВариант.ПолучитьОбъект();
        Объект.Заблокировать();
        Объект.Настройки = Новый ХранилищеЗначения(НовыеНастройки);
        Объект.Записать();
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

ВызватьИсключение без параметров пробрасывает ошибку выше. Там ее ловит внешний цикл, пишет в журнал и идет к следующему варианту. Транзакция здесь короткая: одна запись — одна фиксация.

Ошибки при программном обновлении и их перехват

Каждый вариант обрабатывайте в своей попытке. Один битый вариант не должен останавливать весь проход.

Попытка
    ОбновитьОдинВариант(СсылкаНаВариант, Схема);
    Обновлено = Обновлено + 1;
Исключение
    Текст = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
    ЗаписьЖурналаРегистрации("Обновление вариантов отчетов",
        УровеньЖурналаРегистрации.Ошибка, , СсылкаНаВариант, Текст);
    Ошибок = Ошибок + 1;
КонецПопытки;

ПодробноеПредставлениеОшибки доступна в платформе с номером не ниже 8.3.17. На более ранних платформах берите ОписаниеОшибки().

Диагностика: выберите симптом

Отдельный случай — вариант от отчета, которого уже нет в конфигурации. Ссылка в реквизите Отчет ведет на идентификатор удаленного объекта, и Отчеты[ИмяОтчета] упадет. Проверяйте наличие объекта через Метаданные.Отчеты.Найти(ИмяОтчета) до обращения к менеджеру.

Массовое обновление вариантов отчетов: как не положить базу

На базе с сотней пользователей вариантов бывает несколько тысяч. Прямой цикл по справочнику с открытием каждого объекта работает медленно и держит блокировки. Что ускоряет проход:

  • 🎯 Выбирайте только нужное запросом. Фильтр по отчету, Пользовательский = Истина, ПометкаУдаления = Ложь. Объект получайте только для тех, где реально нашлась замена.
  • 🗂 Кешируйте схему. Одна схема на отчет, сложенная в Соответствие по полному имени. Получение макета для каждого варианта заново — лишние обращения к метаданным.
  • 🔒 Одна транзакция — один вариант. Длинная транзакция на тысячу записей держит блокировки и при ошибке откатывает всё.
  • 🕗 Запускайте вне рабочего времени. Тогда меньше конфликтов блокировки.

Запрос, который отбирает пользовательские варианты одного отчета:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    ВариантыОтчетов.Ссылка КАК Ссылка,
|    ВариантыОтчетов.Настройки КАК Настройки
|ИЗ
|    Справочник.ВариантыОтчетов КАК ВариантыОтчетов
|ГДЕ
|    ВариантыОтчетов.Пользовательский
|    И НЕ ВариантыОтчетов.ПометкаУдаления
|    И ВЫРАЗИТЬ(ВариантыОтчетов.Отчет КАК Справочник.ИдентификаторыОбъектовМетаданных).ПолноеИмя = &ПолноеИмя";
Запрос.УстановитьПараметр("ПолноеИмя", "Отчет.ОборотноСальдоваяВедомость");
Выборка = Запрос.Выполнить().Выбрать();

Поле Настройки в выборке уже содержит ХранилищеЗначения. Вызывайте Выборка.Настройки.Получить(), проверяйте, нужна ли замена, и только потом открывайте объект для записи. Так база не перезаписывает сотни вариантов, которые менять не нужно.

Как сравнить настройки до и после

Для отладки удобно сериализовать настройки в XML и сравнить строки:

Функция НастройкиВСтроку(Настройки)
    Запись = Новый ЗаписьXML;
    Запись.УстановитьСтроку();
    СериализаторXDTO.ЗаписатьXML(Запись, Настройки);
    Возврат Запись.Закрыть();
КонецФункции

Если строки до и после совпали, запись пропускаете. Этот же прием помогает выгрузить варианты в файлы перед массовой правкой, чтобы при необходимости восстановить их через СериализаторXDTO.ПрочитатьXML.

Перед запуском массового обновления
Выполнено: 0 / 6

Частые вопросы

Можно ли программно изменить предопределенный вариант отчета?
Нет, его настройки берутся из схемы компоновки. Измените вариант в схеме через конфигуратор или переопределите отчет в расширении.
Чем вариант отчета отличается от пользовательских настроек?
Вариант — именованный набор настроек, который видят в списке вариантов. Пользовательские настройки — быстрые отборы и значения, которые форма запоминает за конкретным человеком поверх варианта.
Нужно ли вызывать Восстановить у компоновщика?
Не обязательно. Компоновщик.Восстановить(СпособВосстановленияНастроекКомпоновкиДанных.ПроверятьДоступность) выбрасывает недоступные поля. Это полезно для чистки, но может удалить поле, которое вы собирались переименовать, поэтому вызывайте его после замены.
Как создать новый вариант отчета кодом, а не изменить существующий?
На БСП создайте элемент справочника ВариантыОтчетов: заполните Отчет, КлючВарианта, Наименование, Пользовательский = Истина и Настройки. Без БСП достаточно вызвать ХранилищеВариантовОтчетов.Сохранить с новым ключом.
Почему после обновления вариант пропал из списка у пользователя?
Чаще всего вариант доступен только автору или у него сбилось представление при записи. Проверьте, кому вариант доступен в его свойствах, и что ОписаниеНастроек передано то же, что было при загрузке.