В процессе разработки на платформе «1С:Предприятие 8.3» начинающие программисты часто сталкиваются с необходимостью переноса логики из клиентских процедур на сторону сервера. Это может быть продиктовано требованиями стандартов разработки, необходимостью обращения к данным базы данных или желанием сделать код более структурированным. Однако такой перенос требует понимания механизмов взаимодействия клиента и сервера. Рассмотрим подробно, как это реализовать, какие подводные камни существуют и как оптимизировать этот процесс.
Представим стандартную ситуацию: в документе есть табличная часть Товары. При изменении количества товара нам необходимо автоматически рассчитать общую сумму в строке. Первоначальный код на клиенте выглядит следующим образом:
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
СтрокаТабличнойЧасти = Элементы.Товары.ТекущиеДанные;
СтрокаТабличнойЧасти.Сумма = (СтрокаТабличнойЧасти.Количество * СтрокаТабличнойЧасти.Цена) + (СтрокаТабличнойЧасти.Цена * СтрокаТабличнойЧасти.НДС);
КонецПроцедуры
Важное замечание по логике: Как справедливо заметили участники обсуждения, приведенная выше формула содержит ошибку. Она прибавляет НДС только за одну единицу товара к общей стоимости. Правильный расчет должен учитывать общее количество или рассчитываться на основе суммы без НДС. Мы вернемся к исправлению этой формулы в конце статьи.
Самый простой способ перенести расчет на сервер — это создать серверную процедуру в модуле формы. При использовании директивы &НаСервере процедуре доступен весь контекст формы, включая данные объекта (Объект) и элементы управления (Элементы).
Разберем реализацию по шагам:
&НаСервере
Процедура РассчитатьСуммуНаСервере()
// На сервере нам доступны элементы формы напрямую
ТекДанные = Элементы.Товары.ТекущиеДанные;
Если ТекДанные <> Неопределено Тогда
ТекДанные.Сумма = (ТекДанные.Количество * ТекДанные.Цена) + (ТекДанные.Количество * ТекДанные.Цена * ТекДанные.СтавкаНДС / 100);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
РассчитатьСуммуНаСервере();
КонецПроцедуры
Плюсы: Простота реализации, доступ ко всем реквизитам формы и объекта.
Минусы: Низкая производительность. При каждом вызове &НаСервере вся форма целиком передается с клиента на сервер и обратно. Это называется «серверным раунд-трипом».
Чтобы ускорить работу системы, профессиональные разработчики используют бесконтекстные вызовы. В этом случае на сервер не передается вся форма, а только те параметры, которые мы явно укажем. Это значительно экономит сетевой трафик и ресурсы сервера.
Проанализируем пример реализации:
&НаСервереБезКонтекста
Функция РассчитатьСуммуСтроки(Количество, Цена, СтавкаНДС)
// Здесь нет доступа к Элементы или Объект
Сумма = (Количество * Цена) + (Количество * Цена * СтавкаНДС / 100);
Возврат Сумма;
КонецФункции
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
ТекДанные = Элементы.Товары.ТекущиеДанные;
// Передаем только нужные значения и получаем результат
ТекДанные.Сумма = РассчитатьСуммуСтроки(ТекДанные.Количество, ТекДанные.Цена, ТекДанные.НДС);
КонецПроцедуры
Обратите внимание: В бесконтекстной процедуре нельзя использовать Элементы.Товары.ТекущиеДанные, так как сервер «не знает», что происходит на экране у пользователя. Все данные должны передаваться через аргументы функции.
Выясним причину, по которой опытные разработчики предостерегают от лишних серверных вызовов. В управляемом приложении 1С интерфейс работает на клиенте. Любая арифметическая операция (умножение цены на количество) выполняется процессором клиентского компьютера мгновенно.
Если же мы переносим этот расчет на сервер, программа должна:</1>
Для простых действий вроде 2 * 2 это крайне расточительно. Правило хорошего тона: если для расчета не нужно запрашивать данные из базы (например, остатки на складе или цены из регистра), делайте расчет прямо &НаКлиенте.
Проанализируем формулу расчета. Ошибка в исходном коде автора ((Количество * Цена) + (Цена * НДС)) — типичный пример невнимательности. Рассмотрим, как это реализуется в типовых конфигурациях 1С (УТ 11, ERP, БП 3.0):
Вариант «НДС сверху»:
СуммаБезНДС = Количество * Цена;
СуммаНДС = СуммаБезНДС * (СтавкаНДС / 100);
Всего = СуммаБезНДС + СуммаНДС;
Вариант «НДС в том числе»:
Всего = Количество * Цена;
СуммаНДС = Всего * СтавкаНДС / (100 + СтавкаНДС);
СуммаБезНДС = Всего - СуммаНДС;
Также не забывайте про округление. Для финансовых расчетов всегда используйте функцию Окр(), чтобы избежать появления «лишних» копеек в хвосте числа:
ТекДанные.Сумма = Окр(СуммаРасчетная, 2);
Для того чтобы ваш код работал не только при ручном вводе пользователем в форму, но и при программном создании документов (например, при импорте), расчетную логику рекомендуется выносить в Модуль объекта. В форме же останется только вызов метода объекта.
Рассмотрим, как это выглядит в профессиональной разработке:
1. В модуле документа создаем экспортную процедуру:
Процедура РассчитатьСуммуСтроки(СтрокаТабличнойЧасти) Экспорт
СтрокаТабличнойЧасти.Сумма = СтрокаТабличнойЧасти.Количество * СтрокаТабличнойЧасти.Цена;
// ... логика расчета НДС ...
КонецПроцедуры
2. В модуле формы вызываем её через серверный контекст:
&НаСервере
Процедура РассчитатьНаСервере()
// Создаем объект из данных формы, чтобы вызвать его методы
ДокОбъект = РеквизитФормыВЗначение("Объект");
ТекДанные = Объект.Товары.НайтиПоИдентификатору(Элементы.Товары.ТекущаяСтрока);
ДокОбъект.РассчитатьСуммуСтроки(ТекДанные);
// Копируем изменения обратно в форму
ЗначениеВРеквизитФормы(ДокОбъект, "Объект");
КонецПроцедуры
Такой подход гарантирует единообразие расчетов во всей системе, хотя и является более сложным в реализации для новичка. Начинайте с простых клиентских расчетов и переходите к серверным вызовам только тогда, когда это действительно необходимо для работы с базой данных.