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