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

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

При разработке форм в 1С часто возникает задача автоматического заполнения полей на основании данных, хранящихся в других объектах системы. В нашем случае мы рассмотрим проблему, когда необходимо автоматически получить цену товара из регистра сведений «ЦеныСкладыЕжедневно» и вставить ее в соответствующее поле «Цена» табличной части формы. При этом выбор цены должен осуществляться по заданным фильтрам: «Номенклатура» (Наименование), «Склад» и «Дата».

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

Применяем запросы для извлечения данных

Для получения данных из регистров сведений в 1С наиболее эффективным и правильным способом является использование запросов. Запросы оптимизированы для работы с большими объемами информации, позволяют формировать сложные условия отбора, объединения таблиц и группировки. В отличие от прямых методов, таких как РегистрыСведений.ИмяРегистра.ПолучитьФорму() или Отбор без запроса, запросы гораздо производительнее и гибче.

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

  1. Инициализация объекта Запрос и получение параметров.

    Прежде чем выполнять запрос, нам необходимо получить значения, которые будут использоваться в качестве параметров отбора. Эти значения мы извлекаем из текущей строки табличной части формы (для номенклатуры) и из реквизитов самой формы (для склада и даты). Для доступа к текущей строке табличной части используем свойство ТекущиеДанные элемента формы, а для реквизитов формы – прямое обращение или через ЭлементыФормы.ИмяЭлемента.Значение.

    
    // Получаем текущие данные строки табличной части "Товары"
    СтрокаТабличнойЧасти = ЭлементыФормы.Товары.ТекущиеДанные; 
    
    // Проверяем, выбрана ли номенклатура. Если нет, обнуляем цену и выходим.
    Если СтрокаТабличнойЧасти.Номенклатура = Неопределено Тогда
        СтрокаТабличнойЧасти.Цена = 0;
        Возврат;
    КонецЕсли;
    
    // Получаем значения для параметров из текущей строки табличной части и реквизитов формы.
    // Предполагаем, что СкладОтправитель и Дата - это реквизиты самой формы.
    Номенклатура = СтрокаТабличнойЧасти.Номенклатура; // Значение поля "Номенклатура" из текущей строки
    Склад = ЭтотОбъект.СкладОтправитель;             // Значение реквизита формы "СкладОтправитель"
    ДатаЦены = ЭтотОбъект.Дата;                       // Значение реквизита формы "Дата"
    
    // Создаем новый объект запроса
    Запрос = Новый Запрос;
    
  2. Формирование текста запроса.

    Текст запроса пишется на встроенном языке запросов 1С, который очень похож на SQL. Мы используем операторы ВЫБРАТЬ для указания полей, которые хотим получить, ИЗ для указания источника данных (наш регистр сведений) и ГДЕ для определения условий отбора. Важно использовать псевдонимы для таблиц (например, ЦеныСкладыЕжедневно КАК ЦеныСкладыЕжедневно) и параметры (например, &Номенклатура) для безопасной и эффективной передачи значений.

    
    Запрос.Текст =
    "ВЫБРАТЬ
    | ЦеныСкладыЕжедневно.Цена
    |ИЗ
    | РегистрСведений.ЦеныСкладыЕжедневно КАК ЦеныСкладыЕжедневно
    |ГДЕ
    | ЦеныСкладыЕжедневно.Номенклатура = &Номенклатура
    | И ЦеныСкладыЕжедневно.Склад = &Склад
    | И ЦеныСкладыЕжедневно.Период = &Дата"; // Условие для точного соответствия дате
    

    Обратите внимание, что для периодического регистра сведений поле, по которому осуществляется фильтрация по дате, обычно называется Период.

  3. Установка параметров запроса.

    После того как текст запроса сформирован, мы должны передать в него конкретные значения для наших параметров. Для этого используется метод УстановитьПараметр() объекта Запрос. Критически важно, чтобы имена параметров, указанные в тексте запроса (например, &Номенклатура), точно совпадали с именами, передаваемыми в УстановитьПараметр() (например, "Номенклатура").

    
    // Устанавливаем параметры запроса. Имена параметров должны совпадать
    // с теми, что указаны в тексте запроса (с '&').
    Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
    Запрос.УстановитьПараметр("Склад", Склад);
    Запрос.УстановитьПараметр("Дата", ДатаЦены);
    
  4. Выполнение запроса и обработка результата.

    После установки всех параметров мы выполняем запрос методом Запрос.Выполнить(). Результатом выполнения является объект РезультатЗапроса. Чтобы получить данные из этого результата, мы используем метод Выбрать(), который возвращает объект ВыборкаИзРезультатаЗапроса. Для перебора строк выборки применяем метод Следующий() в цикле или условном операторе Если, если ожидаем одну запись.

    
    РезультатЗапроса = Запрос.Выполнить();
    
    // Если мы ожидаем получить одну запись (одну цену), используем Выбрать().Следующий()
    Выборка = РезультатЗапроса.Выбрать();
    
    // Инициализируем переменную для цены значением по умолчанию (например, 0),
    // на случай если цена не будет найдена
    ЦенаТовара = 0; 
    
    Если Выборка.Следующий() Тогда // Проверяем, есть ли результат в выборке
        ЦенаТовара = Выборка.Цена; // Если есть, получаем значение из поля "Цена"
    КонецЕсли;
    
    // Присваиваем полученную (или дефолтную) цену полю "Цена" в текущей строке таблицы на форме.
    СтрокаТабличнойЧасти.Цена = ЦенаТовара;
    

Работа с периодическими регистрами сведений: "СрезПоследних"

Ваш регистр сведений «ЦеныСкладыЕжедневно» является периодическим, что означает наличие в нем системного измерения Период, хранящего дату записи. В таких регистрах часто требуется получить не цену, установленную точно на указанную дату, а последнюю актуальную цену на эту дату (или до нее). Для этого в 1С существует специальная виртуальная таблица СрезПоследних.

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

Рассмотрим пример запроса с использованием СрезПоследних:


// ... Получение Номенклатуры, Склада, ДатыЦены как в предыдущем примере ...

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЦеныСкладыЕжедневноСрезПоследних.Цена
|ИЗ
| РегистрСведений.ЦеныСкладыЕжедневно.СрезПоследних(&Дата, Номенклатура = &Номенклатура И Склад = &Склад) КАК ЦеныСкладыЕжедневноСрезПоследних";

// Устанавливаем параметры запроса для виртуальной таблицы
// &Дата - это момент времени, на который мы хотим получить срез
// Номенклатура = &Номенклатура И Склад = &Склад - это условия отбора по измерениям
Запрос.УстановитьПараметр("Дата", ДатаЦены);
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Запрос.УстановитьПараметр("Склад", Склад);

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

ЦенаТовара = 0; // Значение по умолчанию

Если Выборка.Следующий() Тогда
    ЦенаТовара = Выборка.Цена;
КонецЕсли;

СтрокаТабличнойЧасти.Цена = ЦенаТовара;

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

Размещение кода и получение данных с формы

Для автоматического заполнения цены, когда пользователь выбирает товар в табличной части, код следует размещать в обработчике события ПриИзменении поля "Номенклатура" (или "Товар") в табличной части формы. Это событие срабатывает каждый раз, когда пользователь выбирает новый товар или изменяет существующий в строке табличной части.

Рассмотрим полный пример процедуры обработчика события ПриИзменении:


// Процедура-обработчик события "ПриИзменении" для поля "Номенклатура"
// табличной части "Товары" на обычной форме.
// Параметр "Элемент" - это сам элемент управления формы (поле "Номенклатура").
Процедура ТоварыНоменклатураПриИзменении(Элемент)

    // Получаем текущие данные строки табличной части "Товары",
    // в которой произошло изменение.
    СтрокаТабличнойЧасти = ЭлементыФормы.Товары.ТекущиеДанные;

    // Важно: если номенклатура не выбрана (например, строка очищена),
    // обнуляем цену и выходим из процедуры.
    Если СтрокаТабличнойЧасти.Номенклатура = Неопределено ИЛИ СтрокаТабличнойЧасти.Номенклатура.Пустая() Тогда
        СтрокаТабличнойЧасти.Цена = 0;
        Возврат;
    КонецЕсли;

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

    // Создаем новый объект запроса
    Запрос = Новый Запрос;

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

    // Устанавливаем параметры запроса.
    // Имена параметров должны точно соответствовать именам, указанным в тексте запроса.
    Запрос.УстановитьПараметр("Дата", ДатаЦены);
    Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
    Запрос.УстановитьПараметр("Склад", Склад);

    // Выполняем запрос
    РезультатЗапроса = Запрос.Выполнить();

    // Получаем выборку из результата запроса.
    // Если ожидаем одну запись, используем Выбрать().Следующий().
    Выборка = РезультатЗапроса.Выбрать();

    // Инициализируем переменную для цены значением по умолчанию (0),
    // чтобы гарантировать наличие значения, даже если цена не будет найдена.
    ЦенаТовара = 0;

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

    // Присваиваем найденную или дефолтную цену в поле "Цена" текущей строки
    // табличной части на форме.
    СтрокаТабличнойЧасти.Цена = ЦенаТовара;

    // Важно: в данном случае СтандартнаяОбработка = Ложь; не требуется,
    // так как мы не отменяем стандартное поведение платформы
    // (например, открытие формы выбора), а просто заполняем поле.
    // Если бы мы хотели отменить стандартную логику, например,
    // при открытии формы выбора, тогда это было бы уместно.

КонецПроцедуры

Нередко в обработчиках событий можно встретить строку СтандартнаяОбработка = Ложь;. Эта строка используется для отмены стандартного поведения платформы. Например, в обработчике выбора она может предотвратить открытие стандартной формы выбора. В нашем случае, когда мы просто заполняем поле, она не является обязательной и не несет функциональной нагрузки, если только нет специфической логики, которую мы хотим отменить.

Что делать, если цена не найдена

В реальных условиях не всегда для каждого товара на каждом складе и на каждую дату найдется запись в регистре цен. Поэтому крайне важно предусмотреть ситуацию, когда запрос не возвращает никакого результата. Если не обработать этот случай, в поле «Цена» может остаться неопределенное значение или возникнет ошибка при попытке обращения к несуществующему полю.

Как мы уже видели в примерах кода, лучшим подходом является инициализация переменной для цены значением по умолчанию (например, 0) перед выполнением запроса. Если запрос успешно найдет цену, это значение будет перезаписано. В противном случае, в поле «Цена» будет установлено значение по умолчанию, что предотвратит ошибки и обеспечит корректное отображение данных на форме.

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

Распространенные ошибки и как их избежать

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

  1. Использование ПолучитьФорму() или прямого отбора регистра для получения данных.

    Как мы уже упоминали, методы типа РегистрыСведений.ИмяРегистра.ПолучитьФорму() предназначены для отображения форм регистров пользователю для интерактивной работы, а не для программного извлечения данных. Прямое обращение к объекту регистра с методом Отбор без запроса может быть медленным и неэффективным, так как данные могут быть сначала выбраны полностью, а затем отфильтрованы. Всегда предпочитайте запросы для программного получения данных.

  2. Динамическое построение текста запроса с помощью функций.

    Попытки создавать текст запроса и устанавливать параметры через множество отдельных функций, как в примере " | " + НоменклатураВыбор(Запрос) + " | И " + СкладВыбор(Запрос) + " | И " + ДатаВыбор(Запрос), являются неоптимальными и приводят к усложнению кода. Параметры должны устанавливаться для одного объекта Запрос до его выполнения, а текст запроса формируется целиком.

  3. Неправильное присвоение результата запроса.

    Код вроде Элемент.Выбрать(Запрос.Выполнить().Выгрузить()); пытается "выбрать" что-то в элементе формы из результата запроса. Это неправильный подход. Результат запроса (РезультатЗапроса) нужно сначала получить, затем из него извлечь конкретные значения (например, с помощью Выборка.Цена), и уже эти значения присвоить конкретным полям в строке табличной части.

  4. Ошибки с именами параметров.

    Убедитесь, что имена параметров, указанные в тексте запроса с символом & (например, &Номенклатура), точно совпадают с именами, передаваемыми в метод Запрос.УстановитьПараметр("Номенклатура", ...). Различия в регистре букв или опечатки приведут к ошибке.

  5. Забытый или некорректно используемый Период.

    Для периодических регистров сведений поле даты называется Период. Если вы пытаетесь фильтровать по другому полю, которое не является измерением Период, вы не получите нужного результата. Всегда используйте Период для точной даты или СрезПоследних для актуальной цены на дату.

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

← На главную