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

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

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

Анализ проблемы: почему пересчет работает медленно

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


&НаКлиенте
Процедура ТоварыЦенаПриИзменении(Элемент)
    Для Каждого СтрокаТаблицыТовары Из Объект.Товары Цикл
        СтрокаТаблицыТовары.ЦенаСНДС = СтрокаТаблицыТовары.Цена + 
            СтрокаТаблицыТовары.Цена * РасчетСтавки(СтрокаТаблицыТовары.СтавкаНДС) / 100;
    КонецЦикла;
КонецПроцедуры

Проблема здесь заключается в том, что при изменении цены всего в одной строке, система вынуждена заново вычислять значения для всех 100 или 500 строк. Ситуация усугубляется, если внутри цикла вызывается функция РасчетСтавки, которая может выполнять серверный вызов. Каждый серверный вызов — это передача данных по сети, что критически замедляет работу интерфейса.

Решение №1: Расчет только текущей строки

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

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


&НаКлиенте
Процедура ТоварыЦенаПриИзменении(Элемент)
    // Получаем строку, которую пользователь редактирует в данный момент
    ТекСтрока = Элементы.Товары.ТекущиеДанные;
    
    Если ТекСтрока <> Неопределено Тогда
        // Выполняем расчет только для этой строки
        СтавкаЧислом = РасчетСтавки(ТекСтрока.СтавкаНДС);
        ТекСтрока.ЦенаСНДС = ТекСтрока.Цена + (ТекСтрока.Цена * СтавкаЧислом / 100);
    КонецЕсли;
КонецПроцедуры

Этот подход позволяет сократить время выполнения операции в десятки раз, так как 1С обрабатывает только один объект в оперативной памяти клиента.

Решение №2: Универсальная процедура пересчета

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

Рассмотрим пошагово, как это реализовать:

  1. Создадим отдельную процедуру для расчета параметров одной строки.
  2. Вызовем её из события ПриИзменении конкретной колонки.
  3. Вызовем её же в цикле, если изменился общий параметр в шапке документа.

Пример реализации такой структуры:


&НаКлиентеНаСервереБезКонтекста
Процедура РассчитатьСтрокуНДС(СтрокаТЧ)
    // Выносим формулу в отдельное место для удобства обслуживания
    // Добавляем округление, чтобы избежать проблем с копейками
    Ставка = ?(СтрокаТЧ.СтавкаНДС = ПредопределенноеЗначение("Перечисление.СтавкиНДС.НДС20"), 20, 0); 
    СтрокаТЧ.ЦенаСНДС = Окр(СтрокаТЧ.Цена + (СтрокаТЧ.Цена * Ставка / 100), 2);
КонецПроцедуры

&НаКлиенте
Процедура ТоварыЦенаПриИзменении(Элемент)
    РассчитатьСтрокуНДС(Элементы.Товары.ТекущиеДанные);
КонецПроцедуры

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

Важные аспекты оптимизации

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

1. Округление (Функция Окр): В финансовых расчетах 1С крайне важно всегда использовать округление. Если этого не делать, при сложении ЦенаСНДС по всем строкам может возникнуть погрешность в несколько копеек из-за скрытых знаков после запятой, что приведет к ошибкам в бухгалтерском учете.

2. Контекстные вызовы: Старайтесь избегать использования функции РасчетСтавки, если она требует обращения к серверу (директива &НаСервере). Посмотрите, можно ли получить значения ставок НДС заранее при открытии формы и сохранить их в структуру на клиенте, чтобы расчет в строках происходил мгновенно без «пинга» до сервера.

3. Видимость колонок: Как было замечено в обсуждении, программное изменение видимости (Элементы.Колонка.Видимость = Ложь) — это ресурсозатратная операция. Если вы меняете видимость в цикле или очень часто, форма будет «моргать». Лучше использовать свойство ТолькоПросмотр или менять видимость один раз при изменении состояния документа, а не для каждой строки отдельно.

Работа с большими данными (тысячи строк)

Если в вашей табличной части тысячи строк, даже цикл &НаКлиенте может подтормаживать. В этом случае проанализируем стратегию переноса расчетов на сервер. Вместо перебора строк на клиенте, можно передать объект на сервер, выполнить расчет с использованием временных таблиц или объектов ТаблицаЗначений, и вернуть результат. На сервере массовые операции с коллекциями выполняются быстрее за счет оптимизации работы с памятью в платформе 1С.

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

← На главную