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