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