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

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

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

В этой статье мы подробно разберем, почему привычные методы "из семерки" или простые обращения через точку не работают в управляемом приложении 1С 8.3 на клиенте, и рассмотрим правильные и оптимизированные способы решения этой задачи.

Понимание проблемы: клиент-серверное разделение

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

Попытка написать код в лоб на клиенте, как показано ниже, приведет к ошибке выполнения:


&НаКлиенте
Процедура ТоварыГостПриИзменении(Элемент)
    ТекущиеДанныеТовары = Элементы.Товары.ТекущиеДанные;
    // ОШИБКА! Обращение через точку на клиенте запрещено
    ТекущиеДанныеТовары.Количествошт = ТекущиеДанныеТовары.Количество / ТекущиеДанныеТовары.Гост.Коэф;
КонецПроцедуры

Выясним причину ошибки: в управляемом приложении на тонком клиенте ссылка на справочник (в нашем случае Гост) представляет собой лишь легкий идентификатор (GUID). База данных со всеми реквизитами физически находится на сервере. Клиент "не знает", какие свойства и реквизиты есть у этой ссылки. Платформа принудительно запрещает обращение через точку вида Ссылка.Реквизит на клиенте, чтобы разработчики не создавали скрытых неоптимальных сетевых запросов к серверу при каждом таком обращении.

Рассмотрим два основных варианта решения этой задачи в зависимости от бизнес-требований.

Вариант 1. Только отображение данных на форме (без сохранения в БД)

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

Разберем по шагам, как настроить отображение такого реквизита в свойствах формы:

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

Платформа автоматически создаст колонку и свяжет ее с данными через путь: Объект.Товары.Гост.Коэф. Данное поле будет обновляться автоматически в режиме "Только чтение" при выборе элемента справочника без написания единой строки кода.

Вариант 2. Использование реквизита в расчетах и запись в базу данных

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

Проанализируем правильную схему реализации с использованием серверного вызова:


&НаКлиенте
Процедура ТоварыГостПриИзменении(Элемент)
    
    ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Вызываем серверную функцию для получения коэффициента
    Коэффициент = ПолучитьКоэффициентНаСервере(ТекущиеДанные.Гост);
    
    // Выполняем расчет на клиенте
    Если Коэффициент <> 0 Тогда
        ТекущиеДанные.Количествошт = ТекущиеДанные.Количество / Коэффициент;
    Иначе
        ТекущиеДанные.Количествошт = 0;
    КонецЕсли;
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьКоэффициентНаСервере(СсылкаНаГост)
    
    Если Не ЗначениеЗаполнено(СсылкаНаГост) Тогда
        Возврат 1;
    КонецЕсли;
    
    // Безопасное и быстрое получение реквизита
    Возврат СсылкаНаГост.Коэф;
    
КонецФункции

Обратите внимание на директиву &НаСервереБезКонтекста. Использование бесконтекстного вызова критически важно: в этом случае на сервер не передаются все данные формы, что значительно ускоряет работу системы и снижает нагрузку на сеть.

Секреты оптимизации производительности при работе на сервере

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

Оптимизация чтения реквизита. В серверной функции выше мы написали: Возврат СсылкаНаГост.Коэф;. Хотя на сервере такое обращение разрешено, оно работает медленно. Платформа при таком обращении неявно считывает из базы данных абсолютно весь объект со всеми его реквизитами и табличными частями. Если справочник "тяжелый", это создает серьезную нагрузку.

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


&НаСервереБезКонтекста
Функция ПолучитьКоэффициентНаСервере(СсылкаНаГост)
    
    Если Не ЗначениеЗаполнено(СсылкаНаГост) Тогда
        Возврат 1;
    КонецЕсли;
    
    // Запрос к СУБД выберет только ОДНО числовое поле, минуя чтение объекта целиком
    Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаГост, "Коэф");
    
КонецФункции

Кэширование данных на клиенте. Если пользователь заполняет строки документа вручную, выбирая один и тот же ГОСТ или одну и ту же номенклатуру, система будет каждый раз выполнять серверный вызов. Чтобы избавиться от лишних запросов к базе данных, можно кэшировать полученные значения в клиентской переменной модуля формы.

Посмотрим на пример реализации клиентского кэша:


&НаКлиенте
Перем КэшКоэффициентов; // Переменная будет хранить соответствие "Ссылка - Коэффициент"

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

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

← На главную