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