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