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

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

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

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

Постановка задачи и разбор типичной ошибки

Представим сценарий: в нашей конфигурации есть документ покупки драгоценных металлов. В его табличной части СписокЗалога присутствует колонка Металл (тип значения — ссылка на перечисление ПеречислениеСсылка.Металлы, содержащее значения Золото и Серебро) и колонка ЦенаЗаГрамм (числовой тип).

Наша цель — сделать так, чтобы при выборе металла в строке автоматически устанавливалась базовая цена: 1500 рублей для золота и 1000 рублей для серебра.

Типичная попытка реализации обработчика события изменения выглядит следующим образом:


&НаКлиенте
Процедура СписокЗалогаМеталлПриИзменении(Элемент)    
    СтрокаТабЧасти = Элементы.СписокЗалога.ТекущиеДанные;
    Если СтрокаТабЧасти.Металл = Перечисления.Металлы.Золото Тогда 
        СтрокаТабЧасти.ЦенаЗаГрамм = 1500;
    Иначе 
        СтрокаТабЧасти.ЦенаЗаГрамм = 1000;
    КонецЕсли;        
КонецПроцедуры

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

Разница между «ТекущиеДанные» и «ТекущаяСтрока»

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

  1. ТекущиеДанные — возвращает объект типа ДанныеФормыЭлементКоллекции. Это фактический контейнер, содержащий данные текущей строки, отображаемой на клиенте. Через него мы можем свободно читать и записывать значения колонок этой строки: например, СтрокаТабЧасти.ЦенаЗаГрамм = 1500. Данное свойство доступно только на клиенте.
  2. ТекущаяСтрока — возвращает идентификатор строки (как правило, числовой индекс или уникальный внутренний ключ строки в коллекции). Это не сам объект с данными, а лишь указатель на него! Если вы попытаетесь написать Элементы.СписокЗалога.ТекущаяСтрока.Металл, то платформа выдаст критическую ошибку: «Значение не является значением объектного типа», так как у обычного числа или идентификатора не может быть реквизита «Металл».

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

Особенности работы с метаданными и перечислениями на клиенте

Вернемся к ошибке «Переменная не определена (Перечисления)». Почему она возникает?

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

Для получения ссылок на предопределенные элементы, включая значения перечислений, на стороне клиента существует специальная встроенная функция платформы — ПредопределенноеЗначение().

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

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

Давайте перепишем наш обработчик события с учетом полученных знаний:


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

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

Архитектурно правильный подход: уходим от жесткого кодирования («хардкода»)

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

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

Проанализируем, как реализовать такую схему правильно, используя контекстно-независимый серверный вызов &НаСервереБезКонтекста, чтобы минимизировать нагрузку на сеть.


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

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

Преимущества профессионального подхода

Реализованная нами схема с вызовом функции &НаСервереБезКонтекста предоставляет ряд важнейших преимуществ:

Используйте эти шаблоны проектирования в своих прикладных решениях, всегда разделяйте клиентскую логику отображения данных и серверную бизнес-логику работы с базой данных!

← На главную