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

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

В процессе разработки и кастомизации конфигураций 1С (например, «Управление торговлей», «ERP Управление предприятием» или «Комплексная автоматизация») программисты часто сталкиваются с необходимостью отобразить в табличной части документа или справочника дополнительную информацию. Это может быть текущий остаток товара на складе, артикул номенклатуры, информация о скидках или любые другие расчетные данные, которые не требуется постоянно хранить в базе данных. Для контроля корректности расчетных данных после доработки учёта можно выполнить автоматическую сверку проводок между базами с помощью COM-соединения.

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

Поймем теорию: как платформа связывает данные и их отображение

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

  1. Создает новый визуальный элемент типа Поле формы (с видом Поле ввода или Поле надписи) внутри элемента-таблицы.
  2. Связывает этот визуальный элемент с реквизитом данных через свойство «Путь к данным» (DataPath). В нашем примере путь к данным будет выглядеть как Объект.Товары.Остаток.

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

Способ 1. Программное заполнение колонок через код

Рассмотрим наиболее гибкий и распространенный способ — программное присвоение значений реквизитам строк. Для обращения к добавленной колонке используется простая конструкция: СтрокаТаблицы.ИмяРеквизита = Значение. Однако контекст выполнения кода и способ получения этой «строки» существенно различаются в зависимости от того, когда именно мы хотим заполнить данные.

Вариант А. Заполнение всей таблицы при открытии формы или чтении документа

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

Проанализируем плохой пример кода, который часто пишут начинающие разработчики:


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

Если в табличной части будет 100 или 200 строк, система выполнит 100–200 отдельных запросов к СУБД, что вызовет серьезные задержки интерфейса. Разберем по шагам правильный, оптимизированный алгоритм заполнения колонок:

  1. Выгрузим все уникальные ссылки на номенклатуру из табличной части в массив.
  2. Одним запросом получим остатки по всем элементам из этого массива.
  3. Поместим результаты во временную коллекцию (например, в объект Соответствие), где ключом будет номенклатура, а значением — остаток.
  4. Быстрым циклом обойдем строки нашей таблицы формы и сопоставим данные из соответствия.

Давайте посмотрим на пример оптимизированной реализации этого алгоритма:


&НаСервере
Процедура ЗаполнитьДополнительныеКолонкиНаСервере()
    
    // Шаг 1. Собираем уникальные ссылки на товары в массив
    МассивТоваров = Новый Массив;
    Для Каждого Строка Из Объект.Товары Цикл
        Если ЗначениеЗаполнено(Строка.Номенклатура) И МассивТоваров.Найти(Строка.Номенклатура) = Неопределено Тогда
            МассивТоваров.Добавить(Строка.Номенклатура);
        EndIf;
    КонецЦикла;
    
    Если МассивТоваров.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // Шаг 2. Получаем данные одним запросом к базе данных
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
        |   ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Остаток
        |ИЗ
        |   РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (&МассивТоваров)) КАК ТоварыНаСкладахОстатки";
    
    Запрос.УстановитьПараметр("МассивТоваров", МассивТоваров);
    
    // Шаг 3. Заполняем соответствие для быстрого поиска в памяти
    СоответствиеОстатков = Новый Соответствие;
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        СоответствиеОстатков.Вставить(Выборка.Номенклатура, Выборка.Остаток);
    КонецЦикла;
    
    // Шаг 4. Обходим строки и записываем значения в добавленный реквизит формы
    Для Каждого Строка Из Объект.Товары Цикл
        НайденныйОстаток = СоответствиеОстатков.Получить(Строка.Номенклатура);
        Строка.Остаток = ?(НайденныйОстаток = Неопределено, 0, НайденныйОстаток);
    КонецЦикла;

КонецПроцедуры

Вариант Б. Динамическое заполнение при изменении данных пользователем

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

Для этого мы используем событие ПриИзменении для соответствующего поля ввода. Нам нужно получить текущую строку, на которой сейчас стоит курсор пользователя. Выясним, как это сделать правильно.

В управляемом интерфейсе у элементов формы типа *Таблица* есть свойство ТекущиеДанные. Оно возвращает объект типа ДанныеФормыКоллекцияЭлемент, который представляет собой ту самую строку, с которой взаимодействует пользователь. Обратите внимание: не путайте свойство ТекущиеДанные со свойством ТекущаяСтрока, так как последнее содержит лишь числовой или уникальный идентификатор строки, а не саму строку с данными.

Посмотрим на пример клиент-серверного взаимодействия при изменении номенклатуры в строке:


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

&НаСервереБезКонтекста
Функция ПолучитьОстатокТовараНаСервере(Знач НоменклатураСсылка)
    
    Если Не ЗначениеЗаполнено(НоменклатураСсылка) Тогда
        Возврат 0;
    КонецЕсли;
    
    // Выполняем точечный запрос для получения остатка конкретного товара
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ЕСТЬNULL(ТоварыНаСкладахОстатки.ВНаличииОстаток, 0) КАК Остаток
        |ИЗ
        |   РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура = &Номенклатура) КАК ТоварыНаСкладахОстатки";
        
    Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
    
    УстановитьПривилегированныйРежим(Истина);
    РезультатЗапроса = Запрос.Выполнить();
    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.Остаток;
    КонецЕсли;
    
    Возврат 0;
    
КонецФункции

Важная деталь: сброс данных при записи и чтении объекта

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

Если добавленный вами реквизит является реквизитом формы, а не реквизитом самого объекта метаданных (документа в конфигураторе), его значения хранятся исключительно в оперативной памяти веб-клиента или тонкого клиента. База данных СУБД ничего не знает про вашу колонку.

Когда пользователь нажимает «Записать», платформа выполняет транзакцию записи на сервере, после чего полностью перечитывает объект из базы данных, чтобы обновить форму актуальными данными из СУБД. В этот момент табличная часть формы перезаполняется «чистыми» данными из базы, а все не связанные с метаданными реквизиты формы сбрасываются в значения по умолчанию.

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

Проанализируем пример правильной привязки обработчиков в модуле управляемой формы:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Заполняем колонки при открытии формы
    ЗаполнилиДополнительныеКолонкиНаСервере();
КонецПроцедуры

&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    // Заполняем колонки при перечитывании объекта из БД
    ЗаполнилиДополнительныеКолонкиНаСервере();
КонецПроцедуры

&НаСервере
Процедура ПослеЗаписиНаСервере(ПараметрыЗаписи)
    // Заполняем колонки после сохранения документа, чтобы предотвратить их обнуление
    ЗаполнилиДополнительныеКолонкиНаСервере();
КонецПроцедуры

Способ 2. Условное оформление (УО) формы

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

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

В условном оформлении формы есть специальное свойство оформления — Текст. Мы можем задать правило: если в строке заполнена номенклатура, то в добавленную колонку ОстатокТекст (которая может быть просто строковым реквизитом) принудительно выводить текст, соответствующий реквизиту номенклатуры.

Рассмотрим, как настроить такое оформление программно в обработчике ПриСозданииНаСервере:


&НаСервере
Процедура НастроитьУсловноеОформлениеДляАртикула()
    
    // Создаем новый элемент условного оформления
    ЭлементОформления = УсловноеОформление.Элементы.Добавить();
    
    // 1. Определяем оформляемое поле (нашу добавленную колонку)
    ПолеОформления = ЭлементОформления.Поля.Элементы.Добавить();
    ПолеОформления.Поле = Новый ПолеКомпоновкиДанных("ТоварыДопКолонкаАртикул");
    
    // 2. Задаем условие: правило применяется, когда Номенклатура в строке заполнена
    ЭлементОтбора = ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Объект.Товары.Номенклатура");
    ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Заполнено;
    
    // 3. Устанавливаем значение свойства "Текст" через связь с реквизитом Номенклатуры
    // В платформе 1С мы можем связать текст оформления напрямую с путем к данным смежного поля
    ЭлементОформления.Установка.УстановитьЗначениеПараметра("Текст", Новый ПолеКомпоновкиДанных("Объект.Товары.Номенклатура.Артикул"));

КонецПроцедуры

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

Сравнительный анализ методов

Давайте сопоставим оба подхода, чтобы четко понимать, какой инструмент выбрать в конкретной ситуации:

  1. Программное присвоение (Способ 1):
    • Плюсы: Возможность реализации абсолютно любых алгоритмов расчета сложности (запросы к регистрам, интеграция через API, сложные математические формулы).
    • Минусы: Необходимость контролировать события жизненного цикла формы (запись, чтение) и писать много кода для предотвращения обнуления данных. Требует ручной оптимизации запросов к БД.
  2. Условное оформление (Способ 2):
    • Плюсы: Высокая скорость работы, минимум кода, отсутствие проблем с очисткой данных при перезаписи документа, автоматическое обновление при интерактивном изменении строк пользователем.
    • Минусы: Подходит только для вывода простых связанных реквизитов (через точку от реквизитов строки) или статического текста. Невозможно использовать для вычисления остатков или цен по сложным формулам.

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

← На главную