Как сохранить расчетные данные в табличном поле при активации строки в обычных формах 1С

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

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

Почему данные «исчезают» из ячеек?

Выясним основную причину проблемы. В обычных формах объект ТабличноеПоле, связанный с динамическим списком (например, ДокументСписок), не хранит значения в расчетных колонках. События ПриВыводеСтроки и ПриПолученииДанных вызываются системой каждый раз, когда строку нужно отрисовать на экране. Если вы программно установили текст в ячейку оформления ОформлениеСтроки.Ячейки.ИмяКолонки.Текст, это значение будет отображаться ровно до тех пор, пока строка видна и не требует перерисовки.

Разберем типичную ошибку в коде автора темы:


Если ФлагРасчета = 1 И ДанныеСтроки.Ссылка = ЭлементыФормы.Список.ТекущаяСтрока Тогда
    // Расчет и вывод данных только для активной строки
КонецЕсли;

Как только пользователь перемещает курсор на другую строку, условие ДанныеСтроки.Ссылка = ЭлементыФормы.Список.ТекущаяСтрока перестает выполняться для всех предыдущих строк. Система перерисовывает их, видит, что условие ложно, и не заполняет текст в ячейках. В результате колонки становятся пустыми. Чтобы данные сохранялись, нам необходимо обеспечить их вывод для всех строк, которые уже были рассчитаны, а не только для текущей.

Решение №1: Использование кэширования ссылок

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

  1. Объявим переменную в начале модуля формы: Перем СписокРассчитанныхДокументов;.
  2. Инициализируем её при открытии: СписокРассчитанныхДокументов = Новый СписокЗначений;.
  3. В событии ПриАктивизацииСтроки будем добавлять текущую ссылку в этот список и инициировать обновление.

Проанализируем пример кода для этого метода:


Процедура СписокПриАктивизацииСтроки(Элемент)
    ТекДанные = Элемент.ТекущиеДанные;
    Если ТекДанные <> Неопределено Тогда
        // Добавляем ссылку в список, если её там еще нет
        Если СписокРассчитанныхДокументов.НайтиПоЗначению(ТекДанные.Ссылка) = Неопределено Тогда
            СписокРассчитанныхДокументов.Добавить(ТекДанные.Ссылка);
        КонецЕсли;
        
        // Заставляем список перерисовать текущую строку
        ЭлементыФормы.Список.ОбновитьСтроки(ТекДанные);
    КонецЕсли;
КонецПроцедуры

Теперь в событии ПриВыводеСтроки мы будем проверять наличие ссылки в нашем кэше:


Процедура СписокПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
    Если СписокРассчитанныхДокументов.НайтиПоЗначению(ДанныеСтроки.Ссылка) <> Неопределено Тогда
        // Здесь выполняется запрос к базе или расчет данных
        // ОформлениеСтроки.Ячейки.Оплачено.Текст = ...
    КонецЕсли;
КонецПроцедуры

Решение №2: Оптимизация через ПриПолученииДанных

Использование ПриВыводеСтроки для запросов к базе данных — плохая практика, так как это событие вызывается для каждой строки отдельно, что порождает «N+1» проблему запросов. Разберем более эффективный способ через ПриПолученииДанных. Это событие получает сразу массив строк, которые будут выведены на экран.

Посмотрим, как реализовать пакетный расчет данных:


Процедура СписокПриПолученииДанных(Элемент, ОформленияСтрок)
    // Собираем ссылки на документы, которые попали в видимую область
    СписокСсылокДляЗапроса = Новый СписокЗначений;
    Для Каждого Оформление Из ОформленияСтрок Цикл
        Если СписокРассчитанныхДокументов.НайтиПоЗначению(Оформление.ДанныеСтроки.Ссылка) <> Неопределено Тогда
            СписокСсылокДляЗапроса.Добавить(Оформление.ДанныеСтроки.Ссылка);
        КонецЕсли;
    КонецЦикла;

    Если СписокСсылокДляЗапроса.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;

    // Выполняем ОДИН запрос ко всем нужным документам
    ТаблицаРезультатов = РассчитатьДанныеЗаказов(СписокСсылокДляЗапроса); 

    // Заполняем оформление строк из полученной таблицы
    Для Каждого Оформление Из ОформленияСтрок Цикл
        СтрокаДанных = ТаблицаРезультатов.Найти(Оформление.ДанныеСтроки.Ссылка, "Сделка");
        Если СтрокаДанных <> Неопределено Тогда
            Оформление.Ячейки.Оплачено.УстановитьТекст(Формат(СтрокаДанных.Оплачено, "ЧДЦ=2"));
            Оформление.Ячейки.Баланс.УстановитьТекст(Формат(СтрокаДанных.Баланс, "ЧДЦ=2"));
            // Настройка цвета фона в зависимости от баланса
            Если СтрокаДанных.Баланс < 0 Тогда
                Оформление.Ячейки.Баланс.ЦветФона = WebЦвета.СветлоРозовый;
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Важные рекомендации по оформлению

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

Проанализируем пример использования Соответствие внутри ПриПолученииДанных:


СоответствиеОформлений = Новый Соответствие;
Для Каждого ТекОформление Из ОформленияСтрок Цикл
    СоответствиеОформлений.Вставить(ТекОформление.ДанныеСтроки.Ссылка, ТекОформление);
КонецЦикла;

// После выполнения запроса:
Пока Выборка.Следующий() Цикл
    Оформление = СоответствиеОформлений[Выборка.Сделка];
    Если Оформление <> Неопределено Тогда
        Оформление.Ячейки.Оплачено.Текст = Выборка.Оплачено;
    КонецЕсли;
КонецЦикла;

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

← На главную