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