Переход с платформы 1С 7.7 или обычных форм 8.2 на управляемые формы часто вызывает трудности у разработчиков, особенно в вопросах взаимодействия клиента и сервера. Одной из типичных задач является получение данных из справочника (например, нормы расхода, цены или веса) при выборе номенклатуры в табличной части документа для последующего автоматического расчета. Рассмотрим подробно, как правильно реализовать этот механизм, соблюдая стандарты разработки и обеспечивая высокую производительность системы.
Прежде чем переходить к написанию кода, разберем ключевое отличие управляемого приложения. В 1С 7.7 или в «обычных» формах 8.x мы могли напрямую обратиться к реквизиту через точку: ТекущаяСтрока.Номенклатура.Норма1. В управляемых формах программный код четко разделен на клиентскую и серверную части.
Клиент (&НаКлиенте) — это то, что видит пользователь. Здесь мы можем управлять элементами формы, выдавать предупреждения, но у нас нет прямого доступа к базе данных. Ссылка на справочник на клиенте — это лишь «идентификатор», пустая оболочка, из которой нельзя «вытащить» значения реквизитов.
Сервер (&НаСервере) — здесь происходит вся работа с данными. Только на сервере мы можем прочитать реальное значение Норма1 из справочника Номенклатура. Однако каждый переход с клиента на сервер — это нагрузка на сеть и время ожидания. Поэтому наша задача — минимизировать количество таких переходов.
Проанализируем ситуацию: расчет должен происходить в тот момент, когда пользователь выбирает товар или меняет количество. Следовательно, нам нужно создать обработчик события ПриИзменении для соответствующих элементов табличной части.
Рассмотрим пример для колонки Номенклатура. В редакторе формы выберем элемент табличной части Номенклатура, в панели свойств найдем событие ПриИзменении и нажмем на лупу. Платформа создаст клиентскую процедуру.
Разберем клиентский код:
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
// Получаем текущую строку табличной части
СтрокаТЧ = Элементы.Товары.ТекущиеДанные;
Если СтрокаТЧ = Неопределено Тогда
Возврат;
КонецЕсли;
// Если номенклатура выбрана, идем на сервер за данными
Если ЗначениеЗаполнено(СтрокаТЧ.Номенклатура) Тогда
// Вызываем серверную функцию для получения нормы
НормаИзСправочника = ПолучитьНормуССервера(СтрокаТЧ.Номенклатура);
// Заполняем реквизит в строке ТЧ
СтрокаТЧ.Норма11 = СтрокаТЧ.Количество * НормаИзСправочника;
КонецЕсли;
КонецПроцедуры
Теперь выясним, как получить данные на стороне сервера. Самый простой (но не самый оптимальный) способ — это обращение к реквизиту через точку. Важно использовать директиву &НаСервереБезКонтекста, если нам не нужно менять другие реквизиты формы, так как это работает быстрее.
&НаСервереБезКонтекста
Функция ПолучитьНормуССервера(ВариантНоменклатуры)
// Обращение через точку заставляет платформу прочитать весь объект из базы
Возврат ВариантНоменклатуры.Норма1;
КонецФункции
Проанализируем, почему этот код может быть неэффективным. Когда мы пишем ВариантНоменклатуры.Норма1, платформа 1С неявно считывает из базы данных весь объект «Номенклатура» со всеми его реквизитами, табличными частями и картинками, чтобы отдать вам всего одно число. Если справочник «тяжелый», это замедлит работу.
Для повышения производительности, особенно в высоконагруженных системах, рекомендуется использовать запрос. Запрос выберет только одно конкретное поле, не загружая лишних данных в память сервера.
Посмотрим на пример оптимизированной функции:
&НаСервереБезКонтекста
Функция ПолучитьНормуССервераОптимизировано(СсылкаНоменклатура)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Норма1 КАК Норма
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", СсылкаНоменклатура);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Норма;
Иначе
Возврат 0;
КонецЕсли;
КонецФункции
Если в вашей конфигурации используется Библиотека стандартных подсистем (БСП), задачу можно решить еще проще и элегантнее, используя встроенные функции общего назначения:
// Пример использования БСП
НормаИзСправочника = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СтрокаТЧ.Номенклатура, "Норма1");
Не забудем, что расчет должен срабатывать и при изменении количества. Чтобы не дублировать код, вынесем логику пересчета в отдельную клиентскую процедуру, которую будем вызывать из разных обработчиков.
&НаКлиенте
Процедура РассчитатьСтрокуТЧ(СтрокаТЧ)
// Предположим, Норма11 — это реквизит ТЧ, где хранится результат
// А значение нормы мы уже подтянули в скрытый или видимый реквизит ТЧ при выборе номенклатуры
СтрокаТЧ.Норма11 = СтрокаТЧ.Количество * СтрокаТЧ.Норма1_Значение;
КонецПроцедуры
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
СтрокаТЧ = Элементы.Товары.ТекущиеДанные;
Если СтрокаТЧ <> Неопределено Тогда
РассчитатьСтрокуТЧ(СтрокаТЧ);
КонецЕсли;
КонецПроцедуры
Разберем еще несколько важных моментов, которые помогут сделать вашу форму «отзывчивой» и профессиональной:
Соответствие на клиенте. Перед вызовом сервера проверяем: если норма для этой номенклатуры уже известна, берем ее из памяти.ОбработкаВыбора элемента формы можно перехватить выбранную ссылку и сразу вызвать серверный расчет.&НаСервере.
&НаСервере
Процедура ПересчитатьВсюТаблицуНаСервере()
Для Каждого Строка Из Объект.Товары Цикл
Строка.Норма11 = Строка.Количество * Строка.Номенклатура.Норма1;
КонецЦикла;
КонецПроцедуры
БезКонтекста) мы работаем напрямую с коллекцией Объект.Товары, а не с элементами формы.СрезПоследних() регистра.Таким образом, мы выяснили, что основная сложность управляемых форм заключается в дисциплине разделения кода. Используя &НаСервереБезКонтекста для получения данных и ТекущиеДанные для манипуляций в строке табличной части, вы сможете реализовать любые сложные алгоритмы расчета, сохраняя при этом высокую скорость работы интерфейса.