Всем доброго дня! Разберем весьма интересную задачу, которую многие разработчики 1С называют «загадкой динамического списка». Проанализируем ситуацию на примере конфигурации 1С:Управление торговлей 11.4, однако подобное поведение встречается и в других современных решениях: ERP, Комплексная автоматизация, ЗУП 3.1.
Допустим, стоит задача добавить дополнительную колонку в форму списка документов. Вы действуете по стандартной и проверенной технологии через расширения:
ЖурналДокументовЗакупки и ее форму СписокДокументов).Для документов продажи все работает отлично: колонка появляется, фильтры применяются. Но при аналогичных действиях с документами закупки происходит непредвиденное. В отладчике в обработчике ПриСозданииНаСервере(После) элемент формы присутствует, и запрос выглядит корректно. Однако к моменту срабатывания ПриОткрытии колонка пропадает из видимости, как по волшебству, а в пользовательских настройках ее просто нет. Выясним причину такого поведения и найдем грамотное решение.
Если данные берутся корректно, права на новые реквизиты есть, а колонка все равно пропадает, первое, что мы должны проверить — не изменяется ли текст запроса программно. Посмотрим на типовой механизм 1С. В сложных журналах документов, объединяющих данные разных типов, платформа часто перестраивает запрос динамического списка на лету.
По стандартам Библиотеки стандартных подсистем (БСП), в визуальном редакторе динамического списка разработчики 1С задают лишь базовый, «заглушечный» запрос. Это нужно, чтобы в конфигураторе была видна структура полей. А вот реальный, рабочий текст собирается программно в процедуре ПриСозданииНаСервере с учетом включенных функциональных опций (ЭДО, регламентированный учет, статусы проверок).
Рассмотрим типовой код, который и удаляет наши модификации, сделанные в визуальном редакторе расширения:
Процедура НастроитьФормуПоВыбраннымОперациямИДокументам(ТЗХозОперацииИТипыДокументов)
// ... получение данных рабочего места ...
СвойстваСписка = ОбщегоНазначения.СтруктураСвойствДинамическогоСписка();
ЗаполнитьЗначенияСвойств(СвойстваСписка, СписокДокументыЗакупки);
// Вот здесь происходит полная замена текста запроса!
СвойстваСписка.ТекстЗапроса = ТекстЗапросаСписокДокументыЗакупки();
// Применение типовых автозамен для статусов
СвойстваСписка.ТекстЗапроса = СтрЗаменить(СвойстваСписка.ТекстЗапроса, "&СтатусПроверки КАК СтатусПроверки", "НЕОПРЕДЕЛЕНО КАК УдаленСтатусПроверки");
СвойстваСписка.ТекстЗапроса = СтрЗаменить(СвойстваСписка.ТекстЗапроса, "&ИндикаторПроверки КАК ИндикаторПроверки", "НЕОПРЕДЕЛЕНО КАК УдаленИндикаторПроверки");
ОбщегоНазначения.УстановитьСвойстваДинамическогоСписка(Элементы.СписокДокументыЗакупки, СвойстваСписка);
КонецПроцедуры
Как мы видим, система полностью переопределяет ТекстЗапроса, вызывая функцию ТекстЗапросаСписокДокументыЗакупки(). Соответственно, все наши визуальные настройки затираются.
На первый взгляд, самое очевидное решение — перехватить функцию, возвращающую системный запрос, с помощью аннотации &Вместо, и вернуть наш модифицированный запрос из расширения:
&Вместо("ТекстЗапросаСписокДокументыЗакупки")
Функция ТекстЗапросаСписокДокументыЗакупки_()
// Опасный код: возвращает визуальный запрос расширения, отсекая типовую логику
Возврат СписокДокументыЗакупки.ТекстЗапроса;
КонецФункции
Проанализируем риски такого подхода: Это крайне небезопасный вариант. Затирая функцию целиком, вы отключаете типовую логику вычисления важных статусов (например, проверки контрагентов или ЭДО). Более того, при первом же обновлении конфигурации, если фирма 1С добавит в журнал новые обязательные параметры или колонки, у пользователей база выдаст критическую ошибку об отсутствии нужных полей.
Вместо жесткой подмены системного запроса, сообщество разработчиков рекомендует применять программную модификацию итогового текста. Разберем два надежных способа.
Суть метода заключается в том, чтобы дождаться, пока типовой код сформирует свой запрос, а затем аккуратно встроить в него наши данные. Создаем расширение для обработчика ПриСозданииНаСервере с аннотацией &После:
&После("ПриСозданииНаСервере")
Процедура Расш_ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ТекстЗапроса = СписокДокументыЗакупки.ТекстЗапроса;
// Аккуратно вставляем нужное поле в секцию ВЫБРАТЬ
ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
"ДокументыЗакупки.Ссылка КАК Ссылка,",
"ДокументыЗакупки.Ссылка КАК Ссылка,
| МойРегистр.МоеДопПоле КАК МоеДопПоле,");
// Добавляем ЛЕВОЕ СОЕДИНЕНИЕ перед секцией ГДЕ или перед концом запроса
ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
"ГДЕ",
"ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.МойРегистр КАК МойРегистр
| ПО ДокументыЗакупки.Ссылка = МойРегистр.Документ
|ГДЕ");
// Применяем изменения
СвойстваСписка = ОбщегоНазначения.СтруктураСвойствДинамическогоСписка();
СвойстваСписка.ТекстЗапроса = ТекстЗапроса;
ОбщегоНазначения.УстановитьСвойстваДинамическогоСписка(Элементы.СписокДокументыЗакупки, СвойстваСписка);
КонецПроцедуры
Таким образом, мы сохраняем весь типовой фундамент запроса, добавляя только свои куски кода.
Если функция формирования запроса слишком сложная и замена строк небезопасна, мы можем использовать &ИзменениеИКонтроль. Это позволяет вставить добавление колонок прямо в исходный код платформы. Платформа сама будет контролировать при обновлениях, не изменился ли критично исходный код 1С.
&ИзменениеИКонтроль("ТекстЗапросаСписокДокументыЗакупки")
Функция Расш_ТекстЗапросаСписокДокументыЗакупки()
ТекстЗапроса = "... типовой текст запроса ...";
#Вставка
ТекстЗапроса = ТекстЗапроса + " ... наше левое соединение и поле ...";
#КонецВставки
Возврат ТекстЗапроса;
КонецФункции
Часто бывает так, что даже при успешной модификации запроса колонка, добавленная визуально через редактор формы в расширении, все равно остается пустой. Это происходит потому, что в момент полной перезаписи запроса типовым кодом свойство ПутьКДанным у визуально созданного элемента может потерять связь с динамическим списком.
Чтобы избежать этого, создадим саму колонку программно в том же обработчике ПриСозданииНаСервере:
НовыйЭлемент = Элементы.Добавить("СписокДокументыЗакупкиМоеДопПоле", Тип("ПолеФормы"), Элементы.СписокДокументыЗакупки);
НовыйЭлемент.Вид = ВидПоляФормы.ПолеНадписи;
НовыйЭлемент.ПутьКДанным = "СписокДокументыЗакупки.МоеДопПоле";
НовыйЭлемент.Заголовок = "Доп. Инфо";
Используя комплексный подход (модификацию строки текста запроса и программное создание элемента формы), вы гарантированно решите загадку пропадающих колонок, сохранив при этом типовой функционал 1С и обезопасив базу при будущих обновлениях.