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

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

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

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

Постановка задачи и анализ структуры

Представим, что у нас есть документ, содержащий табличную часть под названием ПереченьУслугИТоваров. В этой табличной части присутствуют следующие реквизиты (колонки):

  1. УслугаИлиТовар — тип СправочникСсылка.Услуги. При изменении этого поля должна заполняться стоимость.
  2. Стоимость — тип Число. Сюда должна записываться цена, хранящаяся в реквизите справочника.
  3. Количество — тип Число. Заполняется пользователем.
  4. Сумма — тип Число. Рассчитывается автоматически как Количество * Стоимость.

Для реализации логики нам понадобится обработчик события ПриИзменении для поля табличной части, отображающего реквизит УслугаИлиТовар. Разберем по шагам, как это реализовать.

Шаг 1. Создание обработчика события формы

Прежде всего, откроем форму нашего документа, перейдем на вкладку «Элементы» и найдем поле ввода нашей услуги внутри табличной части. В панели свойств найдем раздел «События» и нажмем на лупу рядом с событием ПриИзменении. Платформа предложит нам создать обработчик. Выбираем вариант создания на клиенте.

Рассмотрим шаблон процедуры, который сгенерирует система:


&НаКлиенте
Процедура ПереченьУслугИТоваровУслугаИлиТоварПриИзменении(Элемент)
    // Здесь мы будем писать наш код
КонецПроцедуры

Шаг 2. Получение текущей строки табличной части

Для того чтобы записать стоимость в измененную строку, нам необходимо получить доступ к данным этой строки на клиенте. Для этого используется свойство элемента формы табличной части — ТекущиеДанные.

Проанализируем пример кода получения строки:


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

Важное примечание: Обращение к Элементы.ИмяТабличнойЧасти.ТекущиеДанные возвращает объект строки, содержащий данные колонок именно той строки, в которой сейчас находится курсор пользователя.

Шаг 3. Оптимальное получение стоимости с сервера

Поскольку данные справочников хранятся в базе данных на сервере, а наш обработчик выполняется на клиенте, нам необходимо обратиться к серверу для получения стоимости услуги. Здесь начинающих разработчиков подстерегает серьезная архитектурная ловушка.

Существует два типа серверных вызовов в модуле формы:

Также разберем еще одну частую ошибку: прямое обращение "через точку" вида УслугаСсылка.Стоимость на сервере. В 1С такое обращение приводит к неявному считыванию всего объекта справочника целиком из базы данных в оперативную память. Если в справочнике много реквизитов, прикрепленных файлов или табличных частей, это критически снизит производительность.

Для решения этой проблемы мы применим наиболее эффективные подходы. Посмотрим на два варианта реализации серверной функции.

Вариант А. С использованием функций БСП (Библиотеки Стандартных Подсистем)

Если ваша конфигурация создана на основе типовой (например, УТ, ERP, БП, УНФ) или содержит БСП, используйте стандартный метод получения реквизита без считывания всего объекта:


&НаСервереБезКонтекста
Функция ПолучитьСтоимостьУслуги(УслугаСсылка)
    Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(УслугаСсылка, "Стоимость");
КонецФункции

Вариант Б. Получение значения через облегченный запрос (для самописных конфигураций)

Если БСП в вашей системе отсутствует, напишем точечный запрос к базе данных:


&НаСервереБезКонтекста
Функция ПолучитьСтоимостьУслуги(УслугаСсылка)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Услуги.Стоимость КАК Стоимость
        |ИЗ
        |	Справочник.Услуги КАК Услуги
        |ГДЕ
        |	Услуги.Ссылка = &Ссылка";
        
    Запрос.УстановитьПараметр("Ссылка", УслугаСсылка);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Возврат Выборка.Стоимость;
    КонецЕсли;
    
    Возврат 0;
    
КонецФункции

Шаг 4. Сборка готового решения и расчет суммы

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

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


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

&НаКлиенте
Процедура ПереченьУслугИТоваровКоличествоПриИзменении(Элемент)
    
    СтрокаТЧ = Элементы.ПереченьУслугИТоваров.ТекущиеДанные;
    Если СтрокаТЧ <> Неопределено Тогда
        РассчитатьСуммуСтрокиТЧ(СтрокаТЧ);
    КонецЕсли;
    
КонецПроцедуры

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

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

Продвинутый уровень: Оптимизация с использованием клиентского кэша

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

Проанализируем пример реализации клиентского кэширования цен:


&НаКлиенте
Перем ЛокальныйКэшЦен; // Переменная будет хранить соответствие "Услуга -> Стоимость"

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ЛокальныйКэшЦен = Новый Соответствие;
КонецПроцедуры

&НаКлиенте
Процедура ПереченьУслугИТоваровУслугаИлиТоварПриИзменении(Элемент)
    
    СтрокаТЧ = Элементы.ПереченьУслугИТоваров.ТекущиеДанные;
    Если СтрокаТЧ = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    Если Не ЗначениеЗаполнено(СтрокаТЧ.УслугаИлиТовар) Тогда
        СтрокаТЧ.Стоимость = 0;
        РассчитатьСуммуСтрокиТЧ(СтрокаТЧ);
        Возврат;
    КонецЕсли;
    
    // Инициализируем кэш, если форма открылась в обход ПриОткрытии
    Если ЛокальныйКэшЦен = Неопределено Тогда
        ЛокальныйКэшЦен = Новый Соответствие;
    КонецЕсли;
    
    // Пытаемся взять цену из локальной памяти клиента
    ЦенаИзКэша = ЛокальныйКэшЦен.Получить(СтрокаТЧ.УслугаИлиТовар);
    
    Если ЦенаИзКэша <> Неопределено Тогда
        СтрокаТЧ.Стоимость = ЦенаИзКэша;
    Иначе
        // Если в кэше нет, запрашиваем один раз у сервера и сохраняем в кэш
        ПолученнаяСтоимость = ПолучитьСтоимостьУслуги(СтрокаТЧ.УслугаИлиТовар);
        ЛокальныйКэшЦен.Вставить(СтрокаТЧ.УслугаИлиТовар, ПолученнаяСтоимость);
        СтрокаТЧ.Стоимость = ПолученнаяСтоимость;
    КонецЕсли;
    
    РассчитатьСуммуСтрокиТЧ(СтрокаТЧ);
    
КонецПроцедуры

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

← На главную