Один вызов сервера, происходящий при подстановке цены товара, неизбежен и оправдан. Но обратите внимание на объем данных, передаваемых на сервер и обратно, указанный на рисунке 4.21 (чтобы его увидеть, нужно включить соответствующие опции в окне настройки показателей производительности). Можно ли его уменьшить? Конечно! Действительно, зачем «гонять» весь контекст формы туда и обратно, когда нам нужна всего лишь одна цена товара?
На самом деле в функции для получения актуальной цены РозничнаяЦена() кроме ссылки на выбранный товар используется только один реквизит документа РасходнаяНакладная – Дата. Вместо того чтобы передавать весь контекст формы на сервер, можно передать эти значения в функцию в качестве параметров. Описание функции в модуле формы мы предварим директивой компиляции &НаСервереБезКонтекста. Таким образом мы не будем передавать данные формы на сервер, и наше прикладное решение будет работать быстрее.
И если раньше мы подставляли цену в документ на сервере, то теперь мы будем делать это на клиенте. Именно для этого действия сервер совершенно не нужен, это можно прекрасно сделать прямо на клиенте.
Для этого изменим обработчик события ПриИзменении() поля Товар табличной части документа РасходнаяНакладная следующим образом (листинг 4.10).
Листинг 4.10. Процедура «ТоварыТоварПриИзменении()»
&НаКлиенте
Процедура ТоварыТоварПриИзменении(Элемент)
ДанныеТекущейСтроки = Элементы.Товары.ТекущиеДанные;
ДанныеТекущейСтроки.Цена = РозничнаяЦена(ДанныеТекущейСтроки.Товар, Объект.Дата);
КонецПроцедуры
В этом обработчике мы получаем доступ к данным текущей строки таблицы формы в переменной ДанныеТекущейСтроки и через точку от нее можем обращаться к значению колонок таблицы. Ссылку на выбранный товар (ДанныеТекущейСтроки.Товар) и дату документа (Объект.Дата) мы передаем в функцию для получения актуальной цены РозничнаяЦена() и присваиваем возвращенное значение колонке табличной части Цена.
В модуле формы документа поместим функцию РозничнаяЦена(), выполняющуюся на сервере без контекста формы (листинг 4.11).
Листинг 4.11. Функция «РозничнаяЦена()»
&НаСервереБезКонтекста
Функция РозничнаяЦена(ВыбранныйТовар, АктуальнаяДата)
Отбор = Новый Структура;
Отбор.Вставить("Товар", ВыбранныйТовар);
ЗначенияРесурсов = РегистрыСведений.Цены.ПолучитьПоследнее(АктуальнаяДата, Отбор);
Возврат ЗначенияРесурсов.Цена;
КонецФункции
В этой функции мы создаем структуру Отбор, содержащую отбор по измерению регистра Товар, и устанавливаем отбор равным ссылке на выбранный товар. Затем при помощи метода ПолучитьПоследнее() мы возвращаем актуальную цену выбранного товара.
ПРИМЕЧАНИЕ
Этот пример можно посмотреть в демонстрационной конфигурации «02 (вар. 2) Использование внеконтекстных серверных процедур в модуле формы».
Запустим «1С:Предприятие», откроем расходную накладную и сделаем выбор из справочника товаров в колонке Товар табличной части документа. После этого колонка табличной части Цена автоматически заполнится последней ценой из регистра сведений, актуальной на дату документа для выбранного товара (рис. 4.24).

Рис. 4.24. Подстановка актуальной цены товара в расходную накладную
Как мы видим, функциональность прикладного решения будет такой же, как и в первом случае, но объем передаваемых на сервер данных будет значительно меньше (вместо 2481 – 1886 байт), так как при вызове функции РозничнаяЦена() мы использовали внеконтекстный серверный вызов и не передавали все данные формы на сервер (рис. 4.25).

Рис. 4.25. Показатели производительности
ПРИМЕЧАНИЕ
Заметим, что здесь, так же как и в предыдущем варианте, при первом выборе товара может быть лишний серверный вызов за счет автоматического кеширования формой данных в списке выбора.
Но, с другой стороны, использование процедур с параметрами тоже может иметь свои недостатки. Слишком большое количество параметров в процедуре может снижать производительность. «Слишком большое» – понятие относительное, конкретные числовые оценки дать нельзя. Но, например, если есть выбор: передать 100 параметров или передать всего 2 параметра, – то, конечно же, нужно передавать 2 параметра, а не 100. Поэтому разработчику всегда нужно искать компромисс между здравым смыслом («читабельностью» программы) и оптимизацией.
В связи с этим можно на клиенте формировать структуру параметров, передаваемых в функцию, и затем передавать ее целиком, одним параметром.
В данном конкретном случае мы передаем всего два параметра. Это нормально –можно передавать их по отдельности, и нет необходимости сокращать их количество. Но на этом примере мы покажем, как в принципе можно это сделать. Например, сгруппировать логически связанные между собой параметры в один, который является структурой.
Итак, объединим два параметра, передаваемых в функцию для получения актуальной цены, в структуру, и передадим ее целиком как структуру параметров (листинги 4.12, 4.13).
Листинг 4.12 Процедура «ТоварыТоварПриИзменении()»
&НаКлиенте
Процедура ТоварыТоварПриИзменении(Элемент)
ДанныеТекущейСтроки = Элементы.Товары.ТекущиеДанные;
Отбор = Новый Структура("Товар", ДанныеТекущейСтроки.Товар);
ПараметрыФункции = Новый Структура("Отбор, АктуальнаяДата", Отбор, Объект.Дата);
ДанныеТекущейСтроки.Цена = РозничнаяЦена(ПараметрыФункции);
КонецПроцедуры
Листинг 4.13. Функция «РозничнаяЦена()»
&НаСервереБезКонтекста
Функция РозничнаяЦена(ПараметрыФункции)
ЗначенияРесурсов = РегистрыСведений.Цены.ПолучитьПоследнее(ПараметрыФункции.АктуальнаяДата, ПараметрыФункции.Отбор);
Возврат ЗначенияРесурсов.Цена;
КонецФункции
Обратите внимание, если мы сравним объем передаваемых данных, то оказывается, что он больше, чем когда мы передавали параметры в функцию по отдельности (рис. 4.26).

Рис. 4.26. Показатели производительности
Получается, что для данного примера это действительно ненужная «оптимизация». Как мы и говорили, два параметра в этом примере – это хорошо, и мы даже получили увеличение объема передаваемых данных, когда упаковали их в одну структуру. Но когда параметров слишком много, ситуация может быть обратной. Поэтому в каждой ситуации нужно думать, как лучше поступить, и упаковка параметров в один должна быть логически понятна и объяснима.