СКД: невидимые ловушки трёх слоёв настроек

СКД: невидимые ловушки трёх слоёв настроек

Бухгалтер пишет: «Паша, отчёт сломался. Колонок нет». Открываю тот же отчёт, тот же вариант, та же кнопка «Сформировать». У меня шесть числовых колонок. У неё — две. Один и тот же отчёт, одна и та же база, один и тот же сервер. Разница — учётная запись.

Это был не баг в коде. Не ошибка в запросе. Не проблема с правами доступа. Это была особенность платформы, которую я за 17 лет работы с 1С до конца не осознавал. Три слоя настроек системы компоновки данных, где каждый следующий слой молча перезаписывает предыдущий.

Отчёт СКД: у разработчика видны все 6 колонок, у пользователя — только 2

Контекст: розыгрыш, коды и сверка возвратов

Торговая сеть спортивных товаров. Маркетинговая акция — розыгрыш телевизоров среди покупателей. Механика простая: за каждую покупку клиент получает уникальный код для участия в розыгрыше. При возврате товара код нужно отозвать. Логично: вернул кроссовки — потерял шанс выиграть телевизор.

Бухгалтерии нужен отчёт для сверки. Сколько кодов выдано, сколько отозвано, суммы чеков, суммы возвратов, расхождения. Я сделал отчёт на СКД с отдельным вариантом «Сверка возвратов» — шесть числовых колонок, плоская группировка, всё аккуратно. Проверил под своей учёткой. Работает идеально.

Передал бухгалтеру. Через час — звонок.

Три слоя настроек СКД: схема, вариант, пользователь

Система компоновки данных в 1С хранит настройки не в одном месте. Их три. И они выстроены каскадом — каждый следующий слой может перезаписать предыдущий.

Слой 1: схема компоновки данных. Это то, что разработчик создаёт в конфигураторе. Наборы данных, поля, ресурсы, параметры. Фундамент отчёта. Схема определяет, какие данные вообще доступны. Если поля нет в схеме — его не будет нигде.

Слой 2: вариант отчёта. Настройки конкретного варианта: какие поля выбраны для вывода, какая группировка, какие фильтры по умолчанию, условное оформление. Один отчёт может иметь десяток вариантов — «Детально по клиентам», «Сводная за период», «Сверка возвратов». Каждый вариант — своя конфигурация вывода поверх общей схемы.

Слой 3: пользовательские настройки. То, что платформа сохраняет для каждого пользователя индивидуально. Фильтры, которые пользователь менял. Выбор полей, если он доступен в пользовательских настройках. Группировки, сортировки. Всё, что пользователь когда-либо трогал в форме отчёта.

Каскад работает так: платформа берёт схему, накладывает вариант, а поверх — пользовательские настройки. Что-то вроде CSS: каждый следующий слой переопределяет предыдущий. Только в CSS вы хотя бы видите, какие стили применились. В СКД — нет.

Ловушка: настройки, которых пользователь не создавал

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

Как это произошло в моём случае. Бухгалтер неделю назад открывала тот же отчёт, но другой его вариант — «Выданные коды за период». Там два поля: дата и количество. Она посмотрела данные, закрыла. Платформа сохранила пользовательские настройки: выбор полей — дата и количество.

Через неделю бухгалтер открывает вариант «Сверка возвратов». Платформа загружает вариант — шесть числовых колонок, плоская группировка. Потом накладывает пользовательские настройки. А там — выбор полей из прошлого варианта: дата и количество. Остальные четыре колонки? Их нет в пользовательских настройках. Значит, их нет в отчёте.

Две колонки вместо шести. Не баг. Не ошибка прав. Каскад настроек, работающий ровно так, как задумано платформой.

Я потратил полтора часа, прежде чем понял, в чём дело. Первая мысль — вариант сохранился некорректно. Проверяю хранилище вариантов — всё правильно. Открываю под своей учёткой — шесть колонок. Открываю под учёткой тестового пользователя, который никогда не работал с этим отчётом, — шесть колонок. Открываю под учёткой бухгалтера — две.

Разница — не в варианте. Разница — в истории открытий отчёта конкретным пользователем.

Схема каскада настроек СКД: схема компоновки, вариант отчёта, пользовательские настройки — каждый слой перезаписывает предыдущий

Стандартное решение — «Сбросить настройки» — не работает

Первый порыв — объяснить бухгалтеру: «Нажми кнопку Ещё — Сбросить настройки». Проблема в том, что это не решение. Это перекладывание ответственности.

Бухгалтер не должна знать про каскад настроек СКД. Она открывает вариант отчёта и ожидает увидеть то, что в нём заложено. Шесть колонок для сверки. Не два поля от прошлого варианта, который она уже и не помнит.

Хуже того: кнопка «Сбросить настройки» сбрасывает вообще всё. Включая фильтры по периоду и по магазину, которые бухгалтер настраивала под себя и которые ей нужны. Одно лечим — другое ломаем.

Я видел в разных проектах попытки решить эту проблему через инструкции пользователям. «Если отчёт выглядит не так — сбросьте настройки». Через неделю инструкцию забывают. Через месяц снова звонки: «Паша, отчёт сломался».

Нужно программное решение. Такое, чтобы критически важные варианты отчёта всегда показывали именно те поля, которые в них заложены. Независимо от истории пользователя.

Решение: перехват события ПриЗагрузкеПользовательскихНастроекНаСервере

В форме отчёта есть событие ПриЗагрузкеПользовательскихНастроекНаСервере. Оно срабатывает в момент, когда платформа загружает третий слой — пользовательские настройки — и накладывает их на вариант. Именно здесь можно вмешаться.

Идея: для критических вариантов отчёта принудительно задавать выбор полей из кода. Пользователь может менять фильтры и период, но набор колонок — нет.

&НаСервере
Процедура ПриЗагрузкеПользовательскихНастроекНаСервере(Настройки)
    
    КомпоновщикНастроек = Отчёт.КомпоновщикНастроек;
    НастройкиКД = КомпоновщикНастроек.Настройки;
    
    ТекущийВариант = НастройкиКД.ДополнительныеСвойства.Получить(
        Новый КлючДополнительногоСвойства("ЗаголовокВарианта"));
    
    Если ТекущийВариант = "Сверка возвратов" Тогда
        
        // Очищаем выбранные поля
        НастройкиКД.Выбор.Элементы.Очистить();
        
        // Жёстко задаём 9 полей
        ПоляОтчёта = Новый Массив;
        ПоляОтчёта.Добавить("Клиент");
        ПоляОтчёта.Добавить("ДатаЧека");
        ПоляОтчёта.Добавить("НомерЧека");
        ПоляОтчёта.Добавить("СуммаЧека");
        ПоляОтчёта.Добавить("СуммаВозврата");
        ПоляОтчёта.Добавить("КоличествоВыданныхКодов");
        ПоляОтчёта.Добавить("КоличествоОтозванныхКодов");
        ПоляОтчёта.Добавить("РасхождениеСумм");
        ПоляОтчёта.Добавить("РасхождениеКодов");
        
        Для Каждого ИмяПоля Из ПоляОтчёта Цикл
            ПолеВыбора = НастройкиКД.Выбор.Элементы.Добавить(
                Тип("ВыбранноеПолеКомпоновкиДанных"));
            ПолеВыбора.Поле = Новый ПолеКомпоновкиДанных(ИмяПоля);
        КонецЦикла;
        
        // Сбрасываем группировку в плоскую
        НастройкиКД.Структура.Очистить();
        Группировка = НастройкиКД.Структура.Добавить(
            Тип("ГруппировкаКомпоновкиДанных"));
        // Без указания полей группировки — детальные записи
        
    КонецЕсли;
    
КонецПроцедуры

Тридцать с небольшим строк кода. Что они делают: в момент загрузки пользовательских настроек проверяют, какой вариант открыт. Если это «Сверка возвратов» — полностью очищают выбор полей и пересоздают его из фиксированного списка. Девять полей, зашитых в код. Группировка — плоская, без иерархии.

Пользователь по-прежнему может настраивать фильтры: период, магазин, статус возврата. Но колонки отчёта — неприкосновенны. Именно те шесть числовых, без которых сверка невозможна, плюс три идентифицирующих.

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

Когда применять, а когда — нет

Принудительный сброс полей — не универсальный рецепт. Есть ситуации, когда пользователь должен иметь право менять набор колонок. Аналитические отчёты, где менеджер сам выбирает, какие показатели смотреть. Управленческая отчётность, где каждый руководитель настраивает панель под себя.

Но есть категория отчётов, где набор полей — это не предпочтение, а требование. Сверки. Реестры для налоговой. Печатные формы. Контрольные отчёты, где отсутствие одной колонки делает весь отчёт бесполезным. Для таких отчётов принудительная фиксация полей — обязательна.

Я завёл себе правило: для каждого нового варианта отчёта задаю вопрос — критичен ли набор полей? Если ответ «да» — добавляю обработку ПриЗагрузкеПользовательскихНастроекНаСервере. Пять минут работы, которые экономят часы поддержки. Аналогичный подход — с производительностью запросов в отчётах: разбор LEFT JOIN с неправильным фильтром, который тормозил четыре минуты. СКД генерирует запросы автоматически — и именно поэтому стоит знать, как оптимизировать запросы в 1С, чтобы понимать, что именно замедляет ваш отчёт.

Ещё один приём, который использую в связке: ограничение доступных пользовательских настроек на уровне варианта. В настройках варианта для каждого элемента можно указать, включён ли он в пользовательские настройки. Если выбор полей не вынесен в пользовательские настройки — пользователь физически не может его изменить через интерфейс. Но это не защищает от каскада: пользовательские настройки от предыдущего варианта всё равно могут наложиться.

Почему документация об этом молчит

Формально документация описывает три слоя настроек. На ИТС есть статья про компоновщик настроек, где упоминаются пользовательские настройки. Но нигде — подчёркиваю, нигде — не написано прямым текстом: «Пользовательские настройки от одного варианта могут повлиять на отображение другого варианта того же отчёта».

Это не баг. Это by design. Платформа хранит пользовательские настройки отчёта целиком, а не для каждого варианта отдельно. Когда пользователь переключает вариант, настройки не сбрасываются — они остаются от предыдущего. Потому что с точки зрения платформы это один и тот же отчёт. Просто вариант отображения другой.

Знание об этом передаётся устно. На форумах. В курилке между разработчиками. «Слушай, а ты знал, что пользовательские настройки в СКД шарятся между вариантами?» — «Серьёзно? Нет, не знал». Я сам узнал, потратив полтора часа на отладку. Из той же категории «молчаливых» сбоев — когда стандартное API БСП возвращает пустоту без ошибок.

Таких «подводных камней» в платформе 1С — десятки. Поведение, которое формально корректно, но практически контринтуитивно. И каждый из них — потерянные часы разработчиков по всей стране. Если предположить, что каждый второй разработчик 1С хотя бы раз столкнулся с этой конкретной проблемой и потратил на неё час, — это сотни тысяч человеко-часов, растворившихся в отладке каскада настроек СКД.

Код обработчика ПриЗагрузкеПользовательскихНастроекНаСервере — принудительная фиксация полей варианта отчёта

Чек-лист: как не попасть в ловушку каскада

За годы работы с отчётами на СКД я выработал набор проверок, который запускаю для каждого нового отчёта перед передачей пользователям.

  1. Тестируй под чужой учёткой. Не под своей. Не под администратором. Под учёткой реального пользователя, который уже работал с этим отчётом раньше. Если такой учётки нет — создай тестовую, открой другой вариант отчёта, закрой, потом открой нужный. Воспроизведи каскад.
  2. Для критических вариантов — фиксируй поля в коде. Событие ПриЗагрузкеПользовательскихНастроекНаСервере. Тридцать строк, пять минут, ноль звонков от бухгалтерии.
  3. Не выносите выбор полей в пользовательские настройки без необходимости. Если пользователю не нужно менять набор колонок — не давайте ему такой возможности. Меньше степеней свободы — меньше способов сломать отчёт.
  4. Документируйте варианты. Заведите комментарий в коде с перечнем полей каждого варианта. Когда через полгода придётся разбираться, почему у кого-то «не те колонки» — комментарий сэкономит время.
  5. Проверяйте после обновления. Если вы добавляете новый вариант в существующий отчёт — проверьте, не ломает ли он пользовательские настройки старых вариантов. Новые поля в схеме могут изменить поведение каскада.

Более широкая проблема

История с тремя слоями настроек СКД — частный случай более общей проблемы платформы 1С. Многие механизмы работают неочевидно. Не потому что они плохо спроектированы — у каждого решения есть своя логика. Но эта логика не всегда совпадает с ожиданиями разработчика.

Управляемые блокировки, которые не ставятся при чтении в транзакции. Регламентные задания, которые выполняются в основном потоке, если не указан ключ. Динамический список, который игнорирует порядок полей в запросе. Каждый из этих механизмов документирован. Но документация описывает «как работает», а не «какие грабли ждут». Между этими двумя вещами — пропасть. Про управляемые блокировки и транзакции — отдельный разбор реального бага: как нарушение атомарности заблокировало 47 заказов.

Опыт 1С-разработчика — это не знание синтаксиса и не умение писать запросы. Это коллекция шишек. Каждая шишка — час-два-пять отладки, которые конвертировались в понимание одного неочевидного поведения платформы. Три слоя настроек СКД — ещё одна шишка в мою коллекцию.

Тот бухгалтер, кстати, до сих пор работает с отчётом «Сверка возвратов». Девять колонок. Всегда. Ни разу не позвонила с тех пор. Тридцать строк кода, которые делают ровно то, что платформа должна была делать из коробки — но не делает.