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