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