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

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

При разработке функционала на платформе 1С:Предприятие 8.3 в управляемых формах часто возникает задача отображения в табличной части документа дополнительной информации, которая не хранится в базе данных, а является результатом вычислений. Это так называемые "расчетные" или "виртуальные" колонки. Мы хотим добавить такую колонку программно, чтобы она появлялась только на форме, не требуя изменения структуры объекта конфигурации (документа).

Постановка задачи

Наша цель – создать новую колонку в табличной части документа, например, "Товары", которая будет содержать расчетное значение, такое как "ЦенаСУчетомСкидки". Эта колонка должна быть добавлена динамически во время выполнения программы, без изменения конфигурации базы данных. Она будет отображаться на форме документа и может быть использована для визуализации данных пользователю, но её содержимое не будет сохраняться в базе при записи документа.

Наш первоначальный подход, заключающийся в прямом добавлении колонки через


Элементы.Товары.ПодчиненныеЭлементы.Вставить()
или

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

Основные шаги по добавлению расчетной колонки

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

  1. Создание реквизита формы

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

    Для создания реквизита формы мы используем конструктор объекта РеквизитФормы. При этом критически важно правильно указать его параметры:

    • Имя: Уникальное строковое имя для нашего реквизита (например, "ЦенаСУчетомСкидок").
    • Тип: Описание типа данных, которое будет хранить наш реквизит (например, Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15,2)) для числа с двумя знаками после запятой).
    • Владелец: Это строковый путь к данным, которые являются владельцем данного реквизита. Если колонка добавляется в табличную часть, которая является реквизитом объекта формы (например,
      
      Объект.Товары
      
      ), то владелец будет строкой "Объект.Товары". Распространенной ошибкой является указание здесь элемента формы, например, Элементы.Товары, что приведет к ошибке "Недопустимый путь к реквизиту".
    • Заголовок: Строка, которая будет использоваться как заголовок колонки на форме.
    • ДоступностьДляПользователя: Логическое значение, указывающее, должен ли реквизит быть доступен для пользователя (например, для настройки отображения).

    Пример создания реквизита формы:

    
    МассивРеквизитов = Новый Массив;
    МассивРеквизитов.Добавить(Новый РеквизитФормы("ЦенаСУчетомСкидок", Новый ОписаниеТипов("Число",Новый КвалификаторыЧисла(15,2)), "Объект.Товары", "Цена скид.", Ложь));
    

    Здесь мы создаем реквизит "ЦенаСУчетомСкидок" с типом Число, владельцем "Объект.Товары" (что указывает на табличную часть объекта формы) и заголовком "Цена скид.". Обратите внимание, что мы добавляем его в массив, так как метод ИзменитьРеквизиты принимает массив.

  2. Применение изменений реквизитов формы

    После того как мы создали объект РеквизитФормы, форма еще не знает о его существовании. Для того чтобы форма "приняла" новый реквизит и он стал доступен для использования, нам необходимо вызвать метод ЭтаФорма.ИзменитьРеквизиты(). Этот метод принимает массив объектов РеквизитФормы, которые мы хотим добавить, изменить или удалить.

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

    Пример применения изменений:

    
    ЭтаФорма.ИзменитьРеквизиты(МассивРеквизитов);
    

    Вызов этой процедуры сообщает платформе, что структура данных формы изменилась, и теперь она включает наш новый реквизит "ЦенаСУчетомСкидок" как часть табличной части "Объект.Товары".

  3. Создание элемента формы для отображения

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

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

    Пример создания элемента формы:

    
    Эл = Элементы.Вставить("КолонкаЦенаСУчетомСкидок", Тип("ПолеФормы"), Элементы.Товары, Элементы.ТоварыПроцентАвтоматическойСкидки);
    

    Здесь мы вставляем новый элемент с именем "КолонкаЦенаСУчетомСкидок" (важно, чтобы имя элемента формы не совпадало с именем реквизита формы, хотя платформа обычно их различает по контексту) типа ПолеФормы в табличную часть Элементы.Товары перед колонкой "ПроцентАвтоматическойСкидки".

  4. Привязка элемента формы к данным

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

    Путь должен быть составлен таким образом, чтобы он указывал на наш динамически созданный реквизит внутри табличной части, например, "Объект.Товары.ЦенаСУчетомСкидок". Ошибки здесь могут быть связаны с неверным синтаксисом пути или попыткой привязаться к реквизиту, который не был успешно добавлен через ИзменитьРеквизиты().

    Пример привязки к данным:

    
    Эл.ПутьКДанным = "Объект.Товары.ЦенаСУчетомСкидок";
    

    После этой операции элемент формы "КолонкаЦенаСУчетомСкидок" будет отображать значения, хранящиеся в реквизите "ЦенаСУчетомСкидок" каждой строки табличной части "Объект.Товары".

  5. Настройка свойств элемента формы

    Для того чтобы наша новая колонка выглядела и вела себя корректно, нам нужно настроить её свойства. Для расчетных колонок часто бывает полезно установить свойство ТолькоПросмотр = Истина, чтобы пользователь не мог вручную изменять расчетные значения. Также мы можем указать Вид поля (например, ВидПоляФормы.ПолеВвода) и другие параметры, такие как Заголовок, Ширина и т.д.

    Пример настройки свойств:

    
    Эл.Вид = ВидПоляФормы.ПолеВвода;
    Эл.ТолькоПросмотр = Истина; // Для расчетной колонки часто это необходимо
    

    Таким образом, мы настраиваем внешний вид и поведение колонки, делая её удобной для восприятия пользователем (при необходимости сложного оформления может потребоваться декомпиляция условного оформления).

Пример полной реализации

Соберем все шаги вместе в одном блоке кода. Этот код обычно размещается в обработчике события формы ПриСозданииНаСервере, чтобы колонка создавалась при открытии формы.


// 1. Создание реквизита формы
МассивРеквизитов = Новый Массив;
РеквизитДляКолонки = Новый РеквизитФормы(
    "ЦенаСУчетомСкидок",
    Новый ОписаниеТипов("Число",Новый КвалификаторыЧисла(15,2)),
    "Объект.Товары", // Важно: путь к данным, а не элемент формы
    "Цена скид.",
    Ложь // Недоступно для пользователя в настройках формы
);
МассивРеквизитов.Добавить(РеквизитДляКолонки);

// 2. Применение изменений реквизитов формы
ЭтаФорма.ИзменитьРеквизиты(МассивРеквизитов);

// 3. Создание элемента формы для отображения
// Вставляем новую колонку перед существующей колонкой "ПроцентАвтоматическойСкидки"
ЭлКолонка = Элементы.Вставить("ЦенаСУчетомСкидок_Элемент", Тип("ПолеФормы"), Элементы.Товары, Элементы.ТоварыПроцентАвтоматическойСкидки);

// 4. Привязка элемента формы к данным
ЭлКолонка.ПутьКДанным = "Объект.Товары.ЦенаСУчетомСкидок"; // Важно: полный путь к динамическому реквизиту

// 5. Настройка свойств элемента формы
ЭлКолонка.Вид = ВидПоляФормы.ПолеВвода;
ЭлКолонка.ТолькоПросмотр = Истина; // Расчетная колонка обычно только для просмотра
ЭлКолонка.Заголовок = РеквизитДляКолонки.Заголовок; // Используем заголовок из реквизита

Механизм расчета значения колонки

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

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

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

Важные нюансы и дополнительные возможности

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

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

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

← На главную