Как в запросе 1С получить последнюю цену номенклатуры без конструкции ПЕРВЫЕ?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке печатных форм (например, акта списания ТМЦ или требований-накладных) в конфигурациях на платформе 1С:Предприятие часто возникает задача вывести актуальную стоимость списанных позиций. Если в информационной базе не ведется регламентированный учет цен или отключены типовые механизмы ценообразования, разработчики нередко пытаются извлечь цену напрямую из табличных частей документов поступления. При этом начинающие специалисты часто сталкиваются с синтаксическими ограничениями языка запросов и неоптимальной логикой выборки.

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

Анализ исходной проблемы и ошибок в запросе

В исходном варианте запроса предпринимается попытка соединить табличную часть документа ВнутреннееПотреблениеТоваров.Товары с вложенным запросом к табличной части документов ПоступлениеТоваровУслуг.Товары, используя конструкцию ВЫБРАТЬ ПЕРВЫЕ 100000 ... УПОРЯДОЧИТЬ ПО.

Выделим ключевые ошибки и слабые места такого подхода:

  1. Логическая ошибка с поиском последней цены: Агрегатная функция МАКСИМУМ(ЦЕНА) возвращает наибольшее числовое значение цены за всю историю, а не последнюю цену по времени. Если товар ранее поступал по высокой цене, а в недавнем поступлении его стоимость снизилась, запрос вернет устаревшую завышенную цену. Для получения именно последней цены необходимо ориентироваться на максимальную дату поступления или номер позиции в регистре.
  2. Использование ПЕРВЫЕ для обхода ограничений платформы: Язык запросов 1С запрещает использовать предложение УПОРЯДОЧИТЬ ПО во вложенных запросах без ключевого слова ПЕРВЫЕ. Использование конструкции ПЕРВЫЕ 100000 приводит к полному сканированию таблицы документов поступления в СУБД, создавая избыточную нагрузку и снижая производительность.
  3. Каскадные обращения через точку: Множественные разыменования ссылочных полей вида Д.Ссылка.Организация.Префикс и Д.Номенклатура.ЕдиницаИзмерения в секциях ВЫБРАТЬ и СГРУППИРОВАТЬ ПО порождают множество неявных левых соединений (LEFT JOIN) с таблицами базы данных.
  4. Избыточное использование ПРЕДСТАВЛЕНИЕССЫЛКИ(): Применение функции получения представлений внутри запроса и в секции группировки нагружает сервер СУБД. В типовых сценариях строковые представления рекомендуется формировать при выводе данных в отчет или макет либо соединять поля с наименованиями справочников напрямую.

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

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

  1. Отбираем товары текущего документа: Создаем временную таблицу, куда помещаем список номенклатуры из проводимого или распечатываемого документа списания. Это позволит ограничить объем обрабатываемых данных только нужными позициями.
  2. Определяем дату последнего поступления: Соединяем временную таблицу товаров с документами поступления и вычисляем МАКСИМУМ(Дата) для каждой номенклатуры на дату не позже даты списания.
  3. Получаем документ и цену: Соединяем полученные максимальные даты с табличной частью поступления для извлечения актуальной цены.

Посмотрим на пример реализации данного запроса:


ВЫБРАТЬ
	Д.Ссылка КАК Ссылка,
	Д.НомерСтроки КАК НомерСтроки,
	Д.Номенклатура КАК Номенклатура,
	Д.Количество КАК Количество,
	Д.Ссылка.Дата КАК ДатаДокумента
ПОМЕСТИТЬ ВТ_ТоварыДокумента
ИЗ
	Документ.ВнутреннееПотреблениеТоваров.Товары КАК Д
ГДЕ
	Д.Ссылка В (&МассивОбъектов)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	ВТ_Товары.Номенклатура КАК Номенклатура,
	МАКСИМУМ(ПТУ.Ссылка.Дата) КАК МаксДатаПоступления
ПОМЕСТИТЬ ВТ_ПоследниеПоступления
ИЗ
	ВТ_ТоварыДокумента КАК ВТ_Товары
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровУслуг.Товары КАК ПТУ
		ПО ВТ_Товары.Номенклатура = ПТУ.Номенклатура
			И ПТУ.Ссылка.Дата <= ВТ_Товары.ДатаДокумента
			И ПТУ.Ссылка.Проведен = ИСТИНА
СГРУППИРОВАТЬ ПО
	ВТ_Товары.Номенклатура
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	ВТ_Товары.Ссылка КАК Ссылка,
	ВТ_Товары.НомерСтроки КАК НомерСтроки,
	ВТ_Товары.Номенклатура КАК Номенклатура,
	ВТ_Товары.Количество КАК Количество,
	ЕСТЬNULL(ПТУ.Цена, 0) КАК Цена,
	ЕСТЬNULL(ВТ_Товары.Количество * ПТУ.Цена, 0) КАК Сумма
ИЗ
	ВТ_ТоварыДокумента КАК ВТ_Товары
		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ПоследниеПоступления КАК ПослПоступления
		ПО ВТ_Товары.Номенклатура = ПослПоступления.Номенклатура
		ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровУслуг.Товары КАК ПТУ
		ПО ПослПоступления.Номенклатура = ПТУ.Номенклатура
			И ПослПоступления.МаксДатаПоступления = ПТУ.Ссылка.Дата
			И ПТУ.Ссылка.Проведен = ИСТИНА
УПОРЯДОЧИТЬ ПО
	ВТ_Товары.Ссылка,
	ВТ_Товары.НомерСтроки

Способ 2. Получение цен из регистра накопления «Закупки»

В конфигурациях УТ 11, КА 2 и ERP данные о закупках фиксируются в специализированном регистре накопления Закупки. Обращение к регистру предпочтительнее, чем чтение табличных частей документов, так как регистр оптимизирован по индексам и содержит фактическую стоимость без влияния закрытия месяца.

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


ВЫБРАТЬ
	Д.Ссылка КАК Ссылка,
	Д.НомерСтроки КАК НомерСтроки,
	Д.Номенклатура КАК Номенклатура,
	Д.Количество КАК Количество,
	Д.Ссылка.Дата КАК ДатаДокумента
ПОМЕСТИТЬ ВТ_Товары
ИЗ
	Документ.ВнутреннееПотреблениеТоваров.Товары КАК Д
ГДЕ
	Д.Ссылка В (&МассивОбъектов)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	ВТ_Товары.Номенклатура КАК Номенклатура,
	МАКСИМУМ(Закупки.Период) КАК МаксПериод
ПОМЕСТИТЬ ВТ_МаксДатыЗакупок
ИЗ
	ВТ_Товары КАК ВТ_Товары
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.Закупки КАК Закупки
		ПО ВТ_Товары.Номенклатура = Закупки.АналитикаУчетаНоменклатуры.Номенклатура
			И Закупки.Период <= ВТ_Товары.ДатаДокумента
СГРУППИРОВАТЬ ПО
	ВТ_Товары.Номенклатура
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	ВТ_Товары.Ссылка КАК Ссылка,
	ВТ_Товары.НомерСтроки КАК НомерСтроки,
	ВТ_Товары.Номенклатура КАК Номенклатура,
	ВТ_Товары.Количество КАК Количество,
	ВЫБОР
		КОГДА ЕСТЬNULL(Закупки.Количество, 0) <> 0
			ТОГДА Закупки.Стоимость / Закупки.Количество
		ИНАЧЕ 0
	КОНЕЦ КАК Цена
ИЗ
	ВТ_Товары КАК ВТ_Товары
		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_МаксДатыЗакупок КАК МаксДаты
		ПО ВТ_Товары.Номенклатура = МаксДаты.Номенклатура
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Закупки КАК Закупки
		ПО МаксДаты.Номенклатура = Закупки.АналитикаУчетаНоменклатуры.Номенклатура
			И МаксДаты.МаксПериод = Закупки.Период

Почему регистр «Цены номенклатуры поставщиков» может оставаться пустым

Выясним причину, по которой установка флага «Регистрировать цены поставщика» в документе поступления может не приводить к появлению записей в регистре сведений ЦеныНоменклатурыПоставщиков:

  1. Не указан вид цены поставщика: В УТ 11 для регистрации цен поставщика обязательным условием является указание реквизита ВидЦеныПоставщика. Если в документе поступления не выбрано соглашение или договор с заполненным видом цены, программа не знает, по какому именно виду фиксировать запись, и проведение по регистру сведений пропускается.
  2. Особенности работы без соглашений: Если учет ведется в упрощенном режиме (без использования договоров и индивидуальных соглашений), типовой механизм не может определить контекст регистрации цен. В таком случае для получения актуальных закупочных цен следует использовать получение данных из регистра накопления Закупки либо формировать специализированные документы регистрации цен.
← На главную