Как отловить программное изменение табличной части в управляемых формах 1С

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

В разработке на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с особенностью работы управляемых форм: стандартное событие таблицы ПриИзменении (OnChange) генерируется только в ответ на интерактивные действия пользователя. Если данные в табличной части изменяются программно — например, через заполнение по остаткам, очистку или загрузку из внешнего файла — платформа не инициирует это событие. В результате расчетные итоги, выведенные в подвал таблицы, перестают быть актуальными. Разберем подробно, как решить эту проблему, используя различные архитектурные подходы.

Причина возникновения проблемы

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

Способ 1. Централизованный метод пересчета (Рекомендуемый)

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

Разберем по шагам: сначала опишем процедуру пересчета, а затем интегрируем её во все необходимые места.


&НаКлиенте
Процедура ПересчитатьИтогиТабличнойЧасти()
    
    // Если расчет сложный, вызываем серверный контекст
    СтруктураИтогов = РассчитатьИтогиНаСервере();
    ИтогПоКолонке = СтруктураИтогов.ВсегоСумма;
    
КонецПроцедуры

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

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

Способ 2. Использование платформенных итогов через скрытые колонки

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

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

  1. Добавляем в ТЧ реквизит СуммаДляИтога.
  2. В свойствах элемента формы (колонки) устанавливаем ПутьКДаннымПодвала как Объект.Товары.ИтогСуммаДляИтога.
  3. При программном заполнении таблицы просто устанавливаем значение в этой колонке.

Способ 3. Использование события «ОбновлениеОтображения»

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

Рассмотрим, как минимизировать нагрузку на систему при использовании этого метода. Мы будем сохранять «контрольную сумму» или количество строк таблицы и выполнять пересчет только в том случае, если состояние изменилось.


&НаКлиенте
Перем ПредыдущееСостояниеТЧ; // Переменная модуля формы

&НаКлиенте
Процедура ОбновлениеОтображения(Отказ)
    
    // Сравниваем, например, количество строк и сумму первой строки (или иную метрику)
    ТекущееСостояние = Объект.Товары.Количество();
    
    Если ТекущееСостояние <> ПредыдущееСостояниеТЧ Тогда
        ПересчитатьИтогиТабличнойЧасти();
        ПредыдущееСостояниеТЧ = ТекущееСостояние;
    КонецЕсли;
    
КонецПроцедуры

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

Способ 4. Подключение обработчика ожидания

Если мгновенный пересчет не критичен, можно использовать ПодключитьОбработчикОжидания. Это позволит выполнять проверку и пересчет итогов раз в несколько секунд. Это менее «нервный» для интерфейса способ, чем ОбновлениеОтображения.

Посмотрим на пример реализации:


&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ПодключитьОбработчикОжидания("ПроверитьНеобходимостьПересчета", 2, Ложь);
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьНеобходимостьПересчета()
    // Проверяем модифицированность или другие признаки
    Если ЭтаФорма.Модифицированность Тогда
        ПересчитатьИтогиТабличнойЧасти();
    КонецЕсли;
КонецПроцедуры

Способ 5. Использование расширений для перехвата методов

В современных версиях 1С (8.3.6 и выше) мы можем использовать механизм расширений. Если программное изменение ТЧ происходит в типовых процедурах (например, ЗаполнитьПоОснованию), мы можем создать расширение и добавить код пересчета в методах &После.

Проанализируем ситуацию: это самый правильный способ, если вы работаете с типовой конфигурацией и не хотите снимать объекты с поддержки. Вы просто «подцепляетесь» к процедурам, которые меняют ТЧ, и добавляете свой вызов в самом конце их выполнения.

Резюме и рекомендации

Выбор метода зависит от конкретной задачи:

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

← На главную