Как правильно обойти строки в форме списка 1С и заполнить колонку данными?

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

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

В рамках этой статьи мы подробно разберем, как правильно организовать обход строк в форме списка для обычных форм (платформа 1С 8.2 / 8.3), устраним ошибку итератора, внедрим стопроцентную оптимизацию кода для работы без «зависаний» интерфейса, а также рассмотрим, как аналогичная задача решается в современных управляемых формах с помощью динамических списков.

---

Почему возникает ошибка итератора при обходе элементов формы?

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


// ОШИБОЧНЫЙ КОД (вызовет ошибку итератора)
Для Каждого Строка Из ЭлементыФормы.Список Цикл
    Контрагент = Строка.Наименование;
    // ... дальнейшая логика
КонецЦикла;

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

---

Выбор правильного события: «ПриВыводеСтроки» против «ПриПолученииДанных»

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

  1. ПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки) — данное событие срабатывает индивидуально для каждой отдельно взятой строки в момент ее отрисовки на экране монитора. Если на экране одновременно отображается 20 строк, событие вызовется ровно 20 раз. Использовать это событие для получения данных из базы (через запросы или остатки регистров) категорически запрещено! Это приведет к отправке десятков мелких запросов к СУБД при малейшей прокрутке списка, из-за чего интерфейс начнет сильно тормозить.
  2. ПриПолученииДанных(Элемент, ОформленияСтрок) — это событие отрабатывает один раз для всего пакета (пачки) строк, которые платформа подготовила для вывода на экран. Вместо одиночного оформления строки сюда передается коллекция ОформленияСтрок, содержащая визуальное оформление всех видимых в данный момент строк списка. Это идеальное место для оптимизированного получения данных одним запросом.

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

---

Пошаговый алгоритм оптимизированного заполнения колонки в Обычных формах

Главный закон оптимизации при работе со списками в 1С: никогда не выполнять запросы к базе данных внутри цикла!

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

  1. Проверяем видимость нашей программно или интерактивно добавленной колонки. Если колонка скрыта пользователем настройки формы, нет никакого смысла тратить ресурсы сервера на расчет долгов.
  2. Обходим коллекцию ОформленияСтрок и собираем ссылки на все отображаемые элементы (контрагенты) в массив.
  3. Передаем сформированный массив ссылок в качестве параметра в один-единственный запрос к регистру накопления/сведений.
  4. Результат выполнения этого запроса выгружаем во временную таблицу или, что еще эффективнее, упаковываем в объект Соответствие (где ключом выступает ссылка на контрагента, а значением — сумма долга). Поиск по ключу в объекте Соответствие происходит в оперативной памяти практически мгновенно.
  5. Повторно обходим коллекцию ОформленияСтрок и устанавливаем вычисленные значения в ячейки нашей колонки, получая данные из подготовленного соответствия по ссылке контрагента.

Давайте посмотрим, как выглядит программная реализация этого алгоритма на практике:


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

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

---

Современное решение в Управляемых формах: Динамический список

Если вы работаете в современном управляемом приложении 1С (платформы 8.3), то ручное добавление колонок и написание тяжелого кода обхода строк в обработчиках таблиц ушло в прошлое. На смену старым механизмам пришел мощный инструмент — Динамический список.

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

  1. Откройте форму списка справочника «Контрагенты» в конфигураторе.
  2. Найдите основное свойство формы — реквизит Список (тип ДинамическийСписок).
  3. В панели свойств реквизита установите флаг «Произвольный запрос» и нажмите гиперссылку «Настройка списка».
  4. В открывшемся конструкторе запроса объедините основную таблицу справочника с таблицей остатков нужного регистра через левое соединение. Пример текста запроса динамического списка:

ВЫБРАТЬ
    СправочникКонтрагенты.Ссылка КАК Ссылка,
    СправочникКонтрагенты.Код КАК Код,
    СправочникКонтрагенты.Наименование КАК Наименование,
    ЕСТЬNULL(ВзаиморасчетыОстатки.СуммаВзаиморасчетовОстаток, 0) КАК Задолженность
ИЗ
    Справочник.Контрагенты КАК СправочникКонтрагенты
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки КАК ВзаиморасчетыОстатки
        ПО СправочникКонтрагенты.Ссылка = ВзаиморасчетыОстатки.Контрагент
  1. Платформа 1С автоматически добавит поле Задолженность в список доступных полей схемы компоновки данных. Перетащите это поле мышкой на форму в дерево элементов управления.

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

---

Использование события «ПриПолученииДанныхНаСервере» в 1С 8.3

Иногда возникают нестандартные ситуации, когда данные для вывода в динамический список невозможно извлечь простым запросом через ЛЕВОЕ СОЕДИНЕНИЕ (например, информацию нужно получить по веб-сервису из внешней CRM-системы или рассчитать по сложным формулам). Для таких редких задач в управляемом приложении (начиная с версии платформы 8.3.10) существует серверный аналог старого обработчика — событие ПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки).

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

Применяйте описанные методики, избегайте выполнения запросов внутри циклов и всегда проектируйте форму списка с учетом потенциального роста объема данных вашей информационной базы!

← На главную