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