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