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