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

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

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

Постановка задачи и исходная ситуация

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


&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
    СтрокаТабличнойЧасти = Элементы.Товары.ТекущиеДанные;
    СтрокаТабличнойЧасти.Сумма = (СтрокаТабличнойЧасти.Количество * СтрокаТабличнойЧасти.Цена) + (СтрокаТабличнойЧасти.Цена * СтрокаТабличнойЧасти.НДС);
КонецПроцедуры

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

Вариант 1. Контекстный вызов сервера (&НаСервере)

Самый простой способ перенести расчет на сервер — это создать серверную процедуру в модуле формы. При использовании директивы &НаСервере процедуре доступен весь контекст формы, включая данные объекта (Объект) и элементы управления (Элементы).

Разберем реализацию по шагам:

  1. Оставим на клиенте только вызов серверной процедуры.
  2. На сервере выполним расчет, обращаясь к данным через элементы формы.

&НаСервере
Процедура РассчитатьСуммуНаСервере()
    // На сервере нам доступны элементы формы напрямую
    ТекДанные = Элементы.Товары.ТекущиеДанные;
    Если ТекДанные <> Неопределено Тогда
        ТекДанные.Сумма = (ТекДанные.Количество * ТекДанные.Цена) + (ТекДанные.Количество * ТекДанные.Цена * ТекДанные.СтавкаНДС / 100);
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
    РассчитатьСуммуНаСервере();
КонецПроцедуры

Плюсы: Простота реализации, доступ ко всем реквизитам формы и объекта.

Минусы: Низкая производительность. При каждом вызове &НаСервере вся форма целиком передается с клиента на сервер и обратно. Это называется «серверным раунд-трипом».

Вариант 2. Бесконтекстный вызов сервера (&НаСервереБезКонтекста)

Чтобы ускорить работу системы, профессиональные разработчики используют бесконтекстные вызовы. В этом случае на сервер не передается вся форма, а только те параметры, которые мы явно укажем. Это значительно экономит сетевой трафик и ресурсы сервера.

Проанализируем пример реализации:


&НаСервереБезКонтекста
Функция РассчитатьСуммуСтроки(Количество, Цена, СтавкаНДС)
    // Здесь нет доступа к Элементы или Объект
    Сумма = (Количество * Цена) + (Количество * Цена * СтавкаНДС / 100);
    Возврат Сумма;
КонецФункции

&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
    ТекДанные = Элементы.Товары.ТекущиеДанные;
    // Передаем только нужные значения и получаем результат
    ТекДанные.Сумма = РассчитатьСуммуСтроки(ТекДанные.Количество, ТекДанные.Цена, ТекДанные.НДС);
КонецПроцедуры

Обратите внимание: В бесконтекстной процедуре нельзя использовать Элементы.Товары.ТекущиеДанные, так как сервер «не знает», что происходит на экране у пользователя. Все данные должны передаваться через аргументы функции.

Проблема производительности: когда сервер лишний?

Выясним причину, по которой опытные разработчики предостерегают от лишних серверных вызовов. В управляемом приложении 1С интерфейс работает на клиенте. Любая арифметическая операция (умножение цены на количество) выполняется процессором клиентского компьютера мгновенно.

Если же мы переносим этот расчет на сервер, программа должна:</1>

  1. Собрать данные на клиенте.
  2. Упаковать их в пакет для передачи по сети.
  3. Дождаться ответа от сервера (учитывая задержки сети).
  4. Распаковать данные и обновить экран.

Для простых действий вроде 2 * 2 это крайне расточительно. Правило хорошего тона: если для расчета не нужно запрашивать данные из базы (например, остатки на складе или цены из регистра), делайте расчет прямо &НаКлиенте.

Корректировка бизнес-логики и формул

Проанализируем формулу расчета. Ошибка в исходном коде автора ((Количество * Цена) + (Цена * НДС)) — типичный пример невнимательности. Рассмотрим, как это реализуется в типовых конфигурациях 1С (УТ 11, ERP, БП 3.0):

Вариант «НДС сверху»:


СуммаБезНДС = Количество * Цена;
СуммаНДС = СуммаБезНДС * (СтавкаНДС / 100);
Всего = СуммаБезНДС + СуммаНДС;

Вариант «НДС в том числе»:


Всего = Количество * Цена;
СуммаНДС = Всего * СтавкаНДС / (100 + СтавкаНДС);
СуммаБезНДС = Всего - СуммаНДС;

Также не забывайте про округление. Для финансовых расчетов всегда используйте функцию Окр(), чтобы избежать появления «лишних» копеек в хвосте числа:

ТекДанные.Сумма = Окр(СуммаРасчетная, 2);

Вынесение логики в модули объекта

Для того чтобы ваш код работал не только при ручном вводе пользователем в форму, но и при программном создании документов (например, при импорте), расчетную логику рекомендуется выносить в Модуль объекта. В форме же останется только вызов метода объекта.

Рассмотрим, как это выглядит в профессиональной разработке:

1. В модуле документа создаем экспортную процедуру:


Процедура РассчитатьСуммуСтроки(СтрокаТабличнойЧасти) Экспорт
    СтрокаТабличнойЧасти.Сумма = СтрокаТабличнойЧасти.Количество * СтрокаТабличнойЧасти.Цена;
    // ... логика расчета НДС ...
КонецПроцедуры

2. В модуле формы вызываем её через серверный контекст:


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

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

← На главную