Почему пропадает добавленный реквизит в динамическом списке справочника Номенклатура и как это исправить?

Программист 1С v8.3 (Управляемые формы) 1С:Комплексная автоматизация IT и автоматизация бизнеса
← На главную

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

В этой статье мы подробно разберем, почему возникает это недоразумение в современных типовых конфигурациях (таких как «Комплексная автоматизация 2», «Управление торговлей 11» или «ERP Управление предприятием 2»), выясним внутренние механизмы платформы и БСП, а также рассмотрим два надежных способа решения этой проблемы с примерами готового кода.

Выясняем причину: почему пропадают ручные изменения запроса?

В крупных конфигурациях, построенных на базе БСП (Библиотеки стандартных подсистем), разработчики фирмы «1С» практически полностью отказались от использования статичных (жестко прописанных в конфигураторе) запросов для ключевых динамических списков. Это касается справочников Номенклатура, Партнеры, Контрагенты, а также списков ключевых документов.

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

Программная сборка запроса необходима по нескольким важным причинам:

  1. Функциональные опции: Запрос формируется динамически под конкретные настройки системы. Если в базе отключен учет по характеристикам, сериям или многооборотной таре, то лишние левые соединения и поля просто не будут добавлены в запрос для оптимизации производительности базы данных.
  2. Ограничение доступа (RLS): В зависимости от прав текущего пользователя и настроек RLS, система динамически внедряет условия ограничения доступа прямо в текст запроса.
  3. Стандартный и расширенный поиск: В таких формах на лету переключаются таблицы формы (например, СписокСтандартныйПоискНоменклатура и СписокРасширенныйПоискНоменклатура), и структура запроса меняется в зависимости от режима поиска, выбранного пользователем.

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

---

Способ 1. Программная модификация запроса через Схему запроса (в расширении)

Для того чтобы ваш реквизит гарантированно выводился на форму и не пропадал при обновлениях системы, мы должны внедрить его программно в уже сформированный системой запрос. Лучше всего делать это в расширении конфигурации.

Мы будем использовать объект СхемаЗапроса. Этот объект позволяет безопасно модифицировать структуру запроса (добавлять поля, таблицы и связи), не прибегая к ненадёжной замене строк через СтрЗаменить.

Разберем по шагам, как реализовать этот метод в обработчике ПриСозданииНаСервере формы списка номенклатуры:


&НаСервере
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
    
    // Шаг 1. Получаем схему уже динамически собранного системой запроса
    Схема = Новый СхемаЗапроса;
    Схема.УстановитьТекстЗапроса(СписокНоменклатура.ТекстЗапроса);
    
    // Шаг 2. Выбираем первый пакет запроса (основной запрос списка)
    Пакет = Схема.ПакетЗапросов[0];
    
    // Шаг 3. Добавляем наше поле в выборку. 
    // Предполагаем, что основная таблица справочника в запросе имеет псевдоним "Номенклатура"
    Попытка
        Пакет.Колонки.Добавить("Номенклатура.МойРеквизит", "МойРеквизит");
    Исключение
        ЗаписьЖурналаРегистрации("ДобавлениеРеквизита", УровеньЖурналаРегистрации.Ошибка,,, 
            "Не удалось добавить МойРеквизит в схему запроса: " + ОписаниеОшибки());
        Возврат;
    КонецПопытки;
    
    // Шаг 4. Устанавливаем измененный текст запроса обратно в динамический список
    // Рекомендуется использовать стандартные механизмы БСП для сохранения свойств
    НовыйТекстЗапроса = Схема.ПолучитьТекстЗапроса();
    
    ПараметрыСписка = ОбщегоНазначения.СтруктураСвойствДинамическогоСписка();
    ПараметрыСписка.ТекстЗапроса = НовыйТекстЗапроса;
    ОбщегоНазначения.УстановитьСвойстваДинамическогоСписка(Элементы.СписокНоменклатура, ПараметрыСписка);
    
    // Шаг 5. Программное добавление колонки на форму
    // Так как форма может переключаться между стандартным и расширенным поиском, 
    // определим, какая именно таблица сейчас активна на форме
    ИмяТаблицыФормы = ?(Элементы.Найти("СписокСтандартныйПоискНоменклатура") <> Неопределено, 
                        "СписокСтандартныйПоискНоменклатура", 
                        "СписокНоменклатура");
                        
    ТаблицаФормы = Элементы[ИмяТаблицыФормы];
    
    // Проверим, не добавлена ли колонка ранее
    ИмяКолонки = "СписокМойРеквизит";
    Если Элементы.Найти(ИмяКолонки) = Неопределено Тогда
        НоваяКолонка = Элементы.Добавить(ИмяКолонки, Тип("ПолеФормы"), ТаблицаФормы);
        НоваяКолонка.Вид = ВидПоляФормы.ПолеВвода;
        НоваяКолонка.ПутьКДанным = "СписокНоменклатура.МойРеквизит";
        НоваяКолонка.Заголовок = "Мой реквизит";
        НоваяКолонка.ТолькоПросмотр = Истина;
    КонецЕсли;

КонецПроцедуры

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

---

Способ 2. Заполнение данных через обработчик «ПриПолученииДанныхНаСервере»

Иногда добавление тяжелых полей или левых соединений в основной сложный запрос динамического списка крайне негативно сказывается на производительности системы. Список начинает «тормозить» при прокрутке и поиске. Для таких случаев в платформе 1С (начиная с версии 8.3.10) реализован великолепный альтернативный механизм — событие таблицы списка ПриПолученииДанныхНаСервере.

Этот метод позволяет нам вообще не трогать сложный динамический запрос списка. Мы будем получать и выводить данные «на лету» только для тех строк, которые в данный момент реально отображаются на экране пользователя (обычно это не более 30-45 строк).

Рассмотрим пошагово реализацию этого подхода:

Шаг 1. В конфигураторе (в расширении вашей формы) добавьте новую колонку в таблицу формы списка вручную. Но не привязывайте её к пути данных динамического списка (оставьте свойство ПутьКДанным пустым). Задайте этой колонке имя (например, КолонкаМойРеквизит) и тип значения.

Шаг 2. Создайте обработчик события таблицы списка ПриПолученииДанныхНаСервере в модуле формы расширения:


&НаСервереБезКонтекста
Процедура Расш1_СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
    
    // Шаг 1. Собираем массив ссылок на номенклатуру, которая отображается на экране
    МассивСсылок = Новый Массив;
    Для Каждого СтрокаСписка Из Строки Цикл
        // Ключом в строках динамического списка справочника всегда является Ссылка
        МассивСсылок.Добавить(СтрокаСписка.Ключ);
    EndЦикла;
    
    Если МассивСсылок.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // Шаг 2. Быстрым точечным запросом получаем значения нашего реквизита из базы данных
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Номенклатура.Ссылка КАК Ссылка,
        |   Номенклатура.МойРеквизит КАК МойРеквизит
        |ИЗ
        |   Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        |   Номенклатура.Ссылка В (&МассивСсылок)";
    Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
    
    Выборка = Запрос.Выполнить().Выбрать();
    
    // Шаг 3. Заполняем данными только те строки, которые видит пользователь
    Пока Выборка.Следующий() Цикл
        ТекущаяСтрокаСписка = Строки[Выборка.Ссылка];
        Если ТекущаяСтрокаСписка <> Неопределено Тогда
            // Устанавливаем значение в нашу колонку формы, у которой нет пути к данным
            ТекущаяСтрокаСписка.Данные.Вставить("КолонкаМойРеквизит", Выборка.МойРеквизит);
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

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

← На главную