Как правильно заполнить виртуальную колонку в СправочникСписок и почему тип значения ячейки всегда Неопределено?

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

При разработке в «обычных формах» 1С:Предприятие 8 часто возникает классическая задача: вывести в динамический список справочника или документа дополнительную информацию, которой нет в реквизитах самого объекта. Яркий пример — вывод текущих остатков товаров непосредственно в форме списка справочника Номенклатура.

Для решения этой задачи разработчики добавляют в табличное поле СправочникСписок новую колонку и используют обработчик события ПриПолученииДанных. Однако на этом этапе многие сталкиваются с неожиданным поведением платформы: при попытке обратиться к свойству Значение оформляемой ячейки в отладчике отображается тип Неопределено. В этой статье мы подробно разберем, почему так происходит, как правильно заполнять виртуальные ячейки и как оптимизировать этот процесс, чтобы форма списка не «зависала» при прокрутке.

Выясняем причину: почему в ячейке всегда Неопределено?

Давайте проанализируем поведение платформы. Когда мы добавляем новую колонку на форму в табличное поле, у нее есть ключевое свойство — Данные. Если это свойство заполнено (например, связано с реквизитом справочника Артикул), платформа автоматически связывает ячейку с базой данных, считывает значение нужного типа и заполняет им свойство ячейки Значение еще до того, как будет вызвано событие оформления строки.

Если же мы создаем виртуальную колонку (например, для вывода остатков организации ОстатокОрганизации) и оставляем свойство Данные пустым, то:

  1. Платформа не имеет источника данных для заполнения этой колонки в базе данных.
  2. При инициализации отображения строки свойству ячейки Значение присваивается дефолтный системный тип Неопределено.
  3. Настройка элемента управления колонки (например, установка типа значения «Число» в свойствах колонки формы) влияет исключительно на интерактивное редактирование ячейки пользователем. Она сообщает платформе, какой именно тип данных ожидать при вводе и какой редактор открыть, но она никак не инициализирует пустое значение до момента редактирования.

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

Два способа вывода данных в виртуальную ячейку

Для того чтобы вывести данные в нашу добавленную колонку ОстатокОрганизации, мы можем использовать два альтернативных подхода в обработчике события ПриПолученииДанных. Рассмотрим особенности каждого из них.

Способ 1. Использование свойства Значение

Мы можем напрямую присвоить рассчитанное числовое значение свойству Значение ячейки. В этом случае платформа автоматически применит к нему правила форматирования, заданные в свойствах колонки (например, количество знаков после запятой, выделение отрицательных чисел красным цветом или выравнивание по правому краю).

Пример присвоения значения:


Для Каждого Оформление Из ОформленияСтрок Цикл
    Оформление.Ячейки.ОстатокОрганизации.Значение = ПолученныйОстаток;
КонецЦикла;

Способ 2. Использование свойства Текст или метода УстановитьТекст()

Второй способ — это вывод «сырого» текста непосредственно на экран. При этом свойство Значение так и останется незаполненным (будет содержать Неопределено), но пользователь увидит нужную текстовую строку. Для этого рекомендуется использовать метод УстановитьТекст(), так как он работает производительнее прямого присвоения свойства Текст.

Пример вывода текста:


Для Каждого Оформление Из ОформленияСтрок Цикл
    Оформление.Ячейки.ОстатокОрганизации.УстановитьТекст(Формат(ПолученныйОстаток, "ЧДЦ=0"));
КонецЦикла;

Какой способ выбрать? Если вам важно сохранить системное форматирование чисел и иметь возможность в дальнейшем как-то анализировать тип значения ячейки при интерактивных действиях пользователя, используйте свойство Значение. Если вам нужно просто вывести быстро сформированную строку — используйте метод УстановитьТекст().

Критически важно: оптимизация производительности в ПриПолученииДанных

Просто заполнить виртуальные колонки в цикле — задача несложная. Гораздо сложнее сделать так, чтобы форма списка продолжала быстро работать при прокрутке. Событие ПриПолученииДанных срабатывает очень часто: при открытии формы, при любой прокрутке списка мышью или клавиатурой, при изменении размеров формы или активизации строк.

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

Шаг 1. Проверка видимости колонки

Прежде чем выполнять любые ресурсоемкие операции по расчету остатков, мы должны убедиться, что пользователь вообще видит нашу виртуальную колонку на экране. Если колонка скрыта через настройку списка, рассчитывать данные по ней бессмысленно.


Если Не Элемент.Колонки.ОстатокОрганизации.Видимость Тогда
    Возврат;
КонецЕсли;

Шаг 2. Пакетный сбор идентификаторов строк

Мы должны собрать все ссылки на номенклатуру, которая отображается в текущий момент на экране, в один массив. Это позволит нам обратиться к базе данных ровно один раз.

Шаг 3. Использование механизма Соответствие (Map) для быстрого сопоставления

После выполнения одного общего запроса к регистру остатков по нашему массиву ссылок, мы выгрузим результат в коллекцию Соответствие. Поиск элемента в Соответствие по ключу происходит практически мгновенно ($O(1)$), в отличие от линейного поиска в таблице значений методом Найти().

Рассмотрим комплексный и максимально оптимизированный пример реализации процедуры:


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

Ограничения использования виртуальных колонок

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

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

В современных управляемых формах на платформе 1С v8.3 эти ограничения полностью устранены благодаря использованию Динамических списков с произвольным запросом. Там мы можем легко соединить основную таблицу справочника с виртуальной таблицей остатков на уровне SQL-запроса платформы, что автоматически дает пользователю и сортировку, и группировку, и полноценный отбор по любым рассчитываемым показателям.

← На главную