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

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

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

Разберем типичные ошибки и лучшие практики на основе реального примера кода, который вызвал активное обсуждение в сообществе разработчиков.

Анализ проблемного кода: что не так?

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


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

&НаСервере
Функция ПолучитьРекПоНаим(СсылкаПоНаименованию, Реквизит)
    Возврат СсылкаПоНаименованию[Реквизит];
КонецФункции

Проанализируем ситуацию и выделим основные критические моменты:

  1. Множественные серверные вызовы. Это главная проблема. В примере выше для заполнения четырех полей выполняется четыре полноценных обращения к серверу. В управляемом приложении каждый вызов &НаСервере сопровождается передачей всего контекста формы (данных всех реквизитов и таблиц) туда и обратно. Это создает огромную нагрузку на сеть.
  2. Неверная математика. В исходном коде расчет НДС выглядел так: Объект.НДС = Объект.Итого / 20 * 100. Это логическая ошибка. Если ставка НДС составляет 20%, то налог вычисляется либо как 20% «сверху» (умножение на 0.2), либо как 20% «внутри» (умножение на 20 и деление на 120).
  3. Опечатки в метаданных. Использование строки "Артикуль" вместо "Артикул" приведет к ошибке выполнения, так как платформа не найдет реквизит с таким именем.
  4. Дублирование кода. В процедуре расчета суммы строки часто повторяются одни и те же действия, что усложняет поддержку модуля.

Решение №1: Оптимизация через структуру (без БСП)

Если ваша конфигурация не использует Библиотеку стандартных подсистем (БСП), самый правильный путь — собрать все необходимые данные на сервере за один раз и вернуть их на клиент в виде структуры. Это сократит количество вызовов сервера в разы.

Рассмотрим подробнее реализацию этого метода:


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

&НаСервереБезКонтекста
Функция ПолучитьДанныеНоменклатуры(СсылкаНаТовар)
    
    Результат = Новый Структура;
    Результат.Вставить("Артикул", СсылкаНаТовар.Артикул);
    Результат.Вставить("ЕдиницаИзмерения", СсылкаНаТовар.ЕдиницаИзмерения);
    Результат.Вставить("Цена", СсылкаНаТовар.Цена);
    
    Возврат Результат;
    
КонецФункции

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

Решение №2: Использование возможностей БСП

Если вы работаете в современной конфигурации (УТ 11, ERP, БП 3.0), использование функций БСП является обязательным стандартом качества. Для получения реквизитов объекта существует универсальный механизм в общем модуле ОбщегоНазначения.

Однако здесь есть нюанс, вызвавший спор на форуме. Модуль ОбщегоНазначения имеет флаг «Сервер», но не имеет флага «Вызов сервера». Это означает, что напрямую с клиента вызвать функцию ЗначениеРеквизитаОбъекта нельзя.

Рассмотрим, как правильно организовать этот процесс:

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

Пример вызова через БСП для получения нескольких полей сразу:


// На сервере внутри формы
МассивРеквизитов = "Артикул, ЕдиницаИзмерения, Цена";
Данные = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ТекущаяСтрока.Наименование, МассивРеквизитов);
// Теперь в переменной "Данные" лежит структура с нужными полями

Помните, что функции БСП оптимизированы: они используют кэширование и максимально быстрые запросы к базе данных, что гораздо эффективнее, чем обращение к реквизитам через точку (например, Ссылка.Артикул), которое может привести к неявному считыванию всего объекта из базы.

Правильный расчет суммы и НДС

Выясним причину, по которой расчет налогов часто вызывает затруднения. Необходимо четко разделять логику расчета в строке и по всему документу. Посмотрим на пример корректного пересчета:


&НаКлиенте
Процедура ПересчитатьСуммуСтроки(СтрокаТаблицы)
    
    СтрокаТаблицы.Сумма = СтрокаТаблицы.Количество * СтрокаТаблицы.Цена;
    
    // Пример расчета НДС 20% (включен в сумму)
    СтавкаНДС = 20;
    СтрокаТаблицы.СуммаНДС = Окр(СтрокаТаблицы.Сумма * СтавкаНДС / (100 + СтавкаНДС), 2);
    
КонецПроцедуры

&НаКлиенте
Процедура ПересчитатьИтогиДокумента()
    
    Объект.ИтогоСумма = Объект.СписокТоваров.Итог("Сумма");
    Объект.ИтогоНДС = Объект.СписокТоваров.Итог("СуммаНДС");
    
КонецПроцедуры

Использование метода Итог() на клиенте — это эффективный способ быстро получить сумму по колонке без обхода таблицы в цикле. Это работает быстро, так как данные уже находятся в памяти клиента.

Работа с текущей строкой: Клиент против Сервера

Проанализируем ситуацию, когда расчеты все же необходимо выполнить на сервере (например, если логика получения цены очень сложная и требует запросов к регистрам сведений). Распространенная ошибка — попытка обратиться к Элементы.СписокТоваров.ТекущиеДанные на сервере. Это свойство там недоступно.

В таких случаях мы должны передать на сервер идентификатор строки:


&НаКлиенте
Процедура СписокТоваровНаименованиеПриИзменении(Элемент)
    ИдентификаторСтроки = Элементы.СписокТоваров.ТекущаяСтрока;
    ОбработатьВыборТовараНаСервере(ИдентификаторСтроки);
КонецПроцедуры

&НаСервере
Процедура ОбработатьВыборТовараНаСервере(ИдСтроки)
    ТекСтрока = Объект.СписокТоваров.НайтиПоИдентификатору(ИдСтроки);
    // Теперь здесь можно менять данные строки напрямую в контексте формы
    ТекСтрока.Цена = ... // Логика получения цены
КонецПроцедуры

Подведение итогов

Для создания качественного и быстрого интерфейса в 1С 8.3 придерживайтесь следующих правил:

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

← На главную