Как в управляемой форме документа получить реквизит справочника для расчета в табличной части?

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

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

Понимание архитектуры управляемых форм

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

Клиент (&НаКлиенте) — это то, что видит пользователь. Здесь мы можем управлять элементами формы, выдавать предупреждения, но у нас нет прямого доступа к базе данных. Ссылка на справочник на клиенте — это лишь «идентификатор», пустая оболочка, из которой нельзя «вытащить» значения реквизитов.

Сервер (&НаСервере) — здесь происходит вся работа с данными. Только на сервере мы можем прочитать реальное значение Норма1 из справочника Номенклатура. Однако каждый переход с клиента на сервер — это нагрузка на сеть и время ожидания. Поэтому наша задача — минимизировать количество таких переходов.

Шаг 1: Создание обработчика события на форме

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

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

Разберем клиентский код:


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

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

Шаг 2: Реализация серверной функции

Теперь выясним, как получить данные на стороне сервера. Самый простой (но не самый оптимальный) способ — это обращение к реквизиту через точку. Важно использовать директиву &НаСервереБезКонтекста, если нам не нужно менять другие реквизиты формы, так как это работает быстрее.


&НаСервереБезКонтекста
Функция ПолучитьНормуССервера(ВариантНоменклатуры)
    // Обращение через точку заставляет платформу прочитать весь объект из базы
    Возврат ВариантНоменклатуры.Норма1;
КонецФункции

Проанализируем, почему этот код может быть неэффективным. Когда мы пишем ВариантНоменклатуры.Норма1, платформа 1С неявно считывает из базы данных весь объект «Номенклатура» со всеми его реквизитами, табличными частями и картинками, чтобы отдать вам всего одно число. Если справочник «тяжелый», это замедлит работу.

Шаг 3: Оптимизация получения данных через запрос

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

Посмотрим на пример оптимизированной функции:


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

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


// Пример использования БСП
НормаИзСправочника = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СтрокаТЧ.Номенклатура, "Норма1");

Шаг 4: Комплексный расчет при изменении количества

Не забудем, что расчет должен срабатывать и при изменении количества. Чтобы не дублировать код, вынесем логику пересчета в отдельную клиентскую процедуру, которую будем вызывать из разных обработчиков.


&НаКлиенте
Процедура РассчитатьСтрокуТЧ(СтрокаТЧ)
    
    // Предположим, Норма11 — это реквизит ТЧ, где хранится результат
    // А значение нормы мы уже подтянули в скрытый или видимый реквизит ТЧ при выборе номенклатуры
    СтрокаТЧ.Норма11 = СтрокаТЧ.Количество * СтрокаТЧ.Норма1_Значение;
    
КонецПроцедуры

&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
    СтрокаТЧ = Элементы.Товары.ТекущиеДанные;
    Если СтрокаТЧ <> Неопределено Тогда
        РассчитатьСтрокуТЧ(СтрокаТЧ);
    КонецЕсли;
КонецПроцедуры

Дополнительные рекомендации и лучшие практики

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

  1. Кеширование на клиенте: Если пользователь часто выбирает одни и те же товары в одном документе, можно сохранять полученные нормы в Соответствие на клиенте. Перед вызовом сервера проверяем: если норма для этой номенклатуры уже известна, берем ее из памяти.
  2. Использование ОбработкаВыбора: Иногда удобнее получать данные сразу в момент закрытия формы выбора. В событии ОбработкаВыбора элемента формы можно перехватить выбранную ссылку и сразу вызвать серверный расчет.
  3. Пересчет всей таблицы: Если в шапке документа меняется какой-то глобальный коэффициент, влияющий на все строки, расчет нужно делать в цикле &НаСервере.
    
    &НаСервере
    Процедура ПересчитатьВсюТаблицуНаСервере()
        Для Каждого Строка Из Объект.Товары Цикл
            Строка.Норма11 = Строка.Количество * Строка.Номенклатура.Норма1;
        КонецЦикла;
    КонецПроцедуры
    
    Обратите внимание, что внутри серверной процедуры (без БезКонтекста) мы работаем напрямую с коллекцией Объект.Товары, а не с элементами формы.
  4. Хранение данных: Если «Норма1» — это значение, которое меняется раз в месяц, возможно, стоит хранить его не в справочнике, а в Регистре сведений. В этом случае код будет обращаться к методу СрезПоследних() регистра.

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

← На главную