Каждый разработчик 1С рано или поздно сталкивается с задачей расширения типового функционала. Одной из самых частых задач является добавление новой колонки в форму списка справочника Номенклатура. Казалось бы, задача тривиальная: заходим в конфигуратор, дорабатываем произвольный запрос динамического списка, вытаскиваем нужный реквизит (например, МойРеквизит), переносим его на форму в соответствующую таблицу списка и запускаем систему. Однако в этот момент нас ожидает сюрприз — добавленная колонка просто не отображается на форме в режиме «1С:Предприятие», а при попытке программного обращения к ней платформа выдает ошибку: «Поле объекта не обнаружено».
В этой статье мы подробно разберем, почему возникает это недоразумение в современных типовых конфигурациях (таких как «Комплексная автоматизация 2», «Управление торговлей 11» или «ERP Управление предприятием 2»), выясним внутренние механизмы платформы и БСП, а также рассмотрим два надежных способа решения этой проблемы с примерами готового кода.
В крупных конфигурациях, построенных на базе БСП (Библиотеки стандартных подсистем), разработчики фирмы «1С» практически полностью отказались от использования статичных (жестко прописанных в конфигураторе) запросов для ключевых динамических списков. Это касается справочников Номенклатура, Партнеры, Контрагенты, а также списков ключевых документов.
Когда пользователь открывает форму списка, в обработчике события ПриСозданииНаСервере срабатывает программный код конфигурации (обычно вызывающий функции общих модулей, таких как ПодборТоваровСервер или менеджеры соответствующих справочников). Этот код полностью затирает исходный текст запроса динамического списка и собирает его заново с нуля.
Программная сборка запроса необходима по нескольким важным причинам:
СписокСтандартныйПоискНоменклатура и СписокРасширенныйПоискНоменклатура), и структура запроса меняется в зависимости от режима поиска, выбранного пользователем.В итоге, любые изменения, которые вы внесли руками в свойства динамического списка в конфигураторе, просто бесследно уничтожаются при запуске формы. Так как в заново собранном запросе вашего реквизита нет, платформа удаляет связанную с ним колонку с формы, что и приводит к ошибке работы интерфейса.
---
Для того чтобы ваш реквизит гарантированно выводился на форму и не пропадал при обновлениях системы, мы должны внедрить его программно в уже сформированный системой запрос. Лучше всего делать это в расширении конфигурации.
Мы будем использовать объект СхемаЗапроса. Этот объект позволяет безопасно модифицировать структуру запроса (добавлять поля, таблицы и связи), не прибегая к ненадёжной замене строк через СтрЗаменить.
Разберем по шагам, как реализовать этот метод в обработчике ПриСозданииНаСервере формы списка номенклатуры:
&НаСервере
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
// Шаг 1. Получаем схему уже динамически собранного системой запроса
Схема = Новый СхемаЗапроса;
Схема.УстановитьТекстЗапроса(СписокНоменклатура.ТекстЗапроса);
// Шаг 2. Выбираем первый пакет запроса (основной запрос списка)
Пакет = Схема.ПакетЗапросов[0];
// Шаг 3. Добавляем наше поле в выборку.
// Предполагаем, что основная таблица справочника в запросе имеет псевдоним "Номенклатура"
Попытка
Пакет.Колонки.Добавить("Номенклатура.МойРеквизит", "МойРеквизит");
Исключение
ЗаписьЖурналаРегистрации("ДобавлениеРеквизита", УровеньЖурналаРегистрации.Ошибка,,,
"Не удалось добавить МойРеквизит в схему запроса: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// Шаг 4. Устанавливаем измененный текст запроса обратно в динамический список
// Рекомендуется использовать стандартные механизмы БСП для сохранения свойств
НовыйТекстЗапроса = Схема.ПолучитьТекстЗапроса();
ПараметрыСписка = ОбщегоНазначения.СтруктураСвойствДинамическогоСписка();
ПараметрыСписка.ТекстЗапроса = НовыйТекстЗапроса;
ОбщегоНазначения.УстановитьСвойстваДинамическогоСписка(Элементы.СписокНоменклатура, ПараметрыСписка);
// Шаг 5. Программное добавление колонки на форму
// Так как форма может переключаться между стандартным и расширенным поиском,
// определим, какая именно таблица сейчас активна на форме
ИмяТаблицыФормы = ?(Элементы.Найти("СписокСтандартныйПоискНоменклатура") <> Неопределено,
"СписокСтандартныйПоискНоменклатура",
"СписокНоменклатура");
ТаблицаФормы = Элементы[ИмяТаблицыФормы];
// Проверим, не добавлена ли колонка ранее
ИмяКолонки = "СписокМойРеквизит";
Если Элементы.Найти(ИмяКолонки) = Неопределено Тогда
НоваяКолонка = Элементы.Добавить(ИмяКолонки, Тип("ПолеФормы"), ТаблицаФормы);
НоваяКолонка.Вид = ВидПоляФормы.ПолеВвода;
НоваяКолонка.ПутьКДанным = "СписокНоменклатура.МойРеквизит";
НоваяКолонка.Заголовок = "Мой реквизит";
НоваяКолонка.ТолькоПросмотр = Истина;
КонецЕсли;
КонецПроцедуры
Этот способ гарантирует, что поле будет находиться в структуре динамического списка, и пользователь сможет без проблем выполнять по нему стандартную сортировку, группировку и отбор.
---
Иногда добавление тяжелых полей или левых соединений в основной сложный запрос динамического списка крайне негативно сказывается на производительности системы. Список начинает «тормозить» при прокрутке и поиске. Для таких случаев в платформе 1С (начиная с версии 8.3.10) реализован великолепный альтернативный механизм — событие таблицы списка ПриПолученииДанныхНаСервере.
Этот метод позволяет нам вообще не трогать сложный динамический запрос списка. Мы будем получать и выводить данные «на лету» только для тех строк, которые в данный момент реально отображаются на экране пользователя (обычно это не более 30-45 строк).
Рассмотрим пошагово реализацию этого подхода:
Шаг 1. В конфигураторе (в расширении вашей формы) добавьте новую колонку в таблицу формы списка вручную. Но не привязывайте её к пути данных динамического списка (оставьте свойство ПутьКДанным пустым). Задайте этой колонке имя (например, КолонкаМойРеквизит) и тип значения.
Шаг 2. Создайте обработчик события таблицы списка ПриПолученииДанныхНаСервере в модуле формы расширения:
&НаСервереБезКонтекста
Процедура Расш1_СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
// Шаг 1. Собираем массив ссылок на номенклатуру, которая отображается на экране
МассивСсылок = Новый Массив;
Для Каждого СтрокаСписка Из Строки Цикл
// Ключом в строках динамического списка справочника всегда является Ссылка
МассивСсылок.Добавить(СтрокаСписка.Ключ);
EndЦикла;
Если МассивСсылок.Количество() = 0 Тогда
Возврат;
КонецЕсли;
// Шаг 2. Быстрым точечным запросом получаем значения нашего реквизита из базы данных
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.МойРеквизит КАК МойРеквизит
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В (&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
Выборка = Запрос.Выполнить().Выбрать();
// Шаг 3. Заполняем данными только те строки, которые видит пользователь
Пока Выборка.Следующий() Цикл
ТекущаяСтрокаСписка = Строки[Выборка.Ссылка];
Если ТекущаяСтрокаСписка <> Неопределено Тогда
// Устанавливаем значение в нашу колонку формы, у которой нет пути к данным
ТекущаяСтрокаСписка.Данные.Вставить("КолонкаМойРеквизит", Выборка.МойРеквизит);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Использование метода ПриПолученииДанныхНаСервере — это самый современный, производительный и безопасный способ вывода дополнительной информации в списки. Он минимизирует нагрузку на СУБД, не усложняет основной запрос динамического списка и гарантирует стабильную работу интерфейса без сбоев при обновлениях конфигурации БСП.