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