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

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

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

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

Анализ проблемы: как работает виртуальная таблица «СрезПоследних»

Виртуальная таблица СрезПоследних возвращает самые свежие записи из регистра сведений на указанный момент времени включительно (по условию «Период <= &Период»).

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

  1. Документ поставки был проведен с текущим временем, например 28.03.2024 13:00:00. Запись в регистре сведений ЦеныПоставщиков зафиксировалась именно с этим периодом.
  2. Пользователь открывает форму нового документа ПоступлениеТоваров. По умолчанию реквизит Дата нового объекта может содержать текущую дату с нулевым временем — 28.03.2024 00:00:00.
  3. В обработчике выбора номенклатуры вызывается запрос, куда в качестве параметра периода передается Объект.Дата (то есть начало дня 00:00:00).
  4. Запрос ищет записи, сделанные до или ровно в 00:00:00. Запись, созданная в 13:00:00 того же дня, в выборку не попадает, и функция возвращает пустое значение.

Способ 1. Корректировка передаваемого периода в запросе

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

Посмотрим на пример серверной функции с проверкой переданного периода:


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

Способ 2. Инициализация точной даты при создании документа

Чтобы дата нового документа сразу содержала актуальное время, настроим заполнение реквизитов объекта при его создании.

В модуле объекта документа используем стандартное событие ОбработкаЗаполнения:


Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения, СтандартнаяОбработка)
    
    Если НЕ ЗначениеЗаполнено(Дата) Тогда
        Дата = ТекущаяДатаСеанса();
    КонецЕсли;
    
КонецПроцедуры

Либо в модуле формы документа в обработчике ПриСозданииНаСервере для новых объектов:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    Если Объект.Ссылка.Пустая() И НЕ ЗначениеЗаполнено(Объект.Дата) Тогда
        Объект.Дата = ТекущаяДатаСеанса();
    КонецЕсли;
    
КонецПроцедуры

Способ 3. Получение цены через метод менеджера регистра сведений

Для получения одного значения ресурса из периодического регистра сведений вместо написания текста запроса можно применить встроенный метод ПолучитьПоследнее менеджера регистра сведений.

Разберем реализацию этого метода:


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

Важные рекомендации при работе со срезом цен

  1. Периодичность регистра: Если по правилам предметной области цена поставщика действует в течение всего дня и не может меняться несколько раз в сутки, в свойствах регистра сведений можно установить периодичность В пределах дня. В этом случае время документа перестанет влиять на отбор в рамках одних суток.
  2. Проверка заполнения шапки: Всегда проверяйте заполненность реквизита Контрагент перед запросом цены. Если поставщик не выбран, запрос с отбором вернет пустое значение.
  3. Смена даты или контрагента в шапке: При изменении даты документа или поставщика в шапке цены в табличной части автоматически не обновляются. Необходимо предусмотреть обработчики событий КонтрагентПриИзменении и ДатаПриИзменении с вопросом пользователю о перезаполнении цен в табличной части.
← На главную