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