Как определить номенклатуру и количество товаров, хранящихся на складе более 150 дней в 1С:Управление торговлей 11?

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

При управлении складскими запасами в 1С:Управление торговлей 11 (УТ11) часто возникает задача выявления товаров, которые залежались на складе. Это может быть критически важно для предотвращения порчи, сокращения затрат на хранение, управления оборачиваемостью и своевременного списания неликвидов или брака. Например, как в нашем случае, необходимо оперативно отслеживать брак, который находится на складе более 150 дней, чтобы обеспечить его своевременное списание.

Мы рассмотрим, как правильно сформировать запрос для получения номенклатуры и ее количества, которая находится на определенном складе дольше заданного периода (в нашем случае — более 150 дней).

Анализируем начальную попытку решения и выявляем сложности

Изначально, коллеги могли пытаться решить эту задачу, опираясь на движения регистра ТоварыНаСкладах, что является распространенной ошибкой. Представленный запрос из сообщения 3 является примером такого подхода:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ТоварыНаСкладахОстатки.Склад,
    ТоварыНаСкладахОстатки.Номенклатура,
    ТоварыНаСкладахОстатки.Характеристика
ПОМЕСТИТЬ ТаблицаОстатки
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки( &КонПериода, (Номенклатура В ИЕРАРХИИ (&Номенклатура) ИЛИ &Номенклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) И (Склад В ИЕРАРХИИ (&Склад) ИЛИ &Склад = ЗНАЧЕНИЕ(Справочник.Склады.ПустаяССылка))) КАК ТоварыНаСкладахОстатки
СГРУППИРОВАТЬ ПО
    ТоварыНаСкладахОстатки.Склад,
    ТоварыНаСкладахОстатк­и.Номенклатура,
    ТоварыНаСкладахОстатк­и.Характеристика
;

//////////////////////////////////////////////////////////////

ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ТоварыНаСкладах.Склад,
    ТоварыНаСкладах.Номенклатура КАК Номенклатура,
    ТоварыНаСкладах.Характеристика КАК ХарактеристикаНоменклатуры,
    СУММА(ТоварыНаСкладах.ВНаличии) КАК Количество,
    НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ) КАК Период,
    ТоварыНаСкладах.Регистратор,
    РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ), &КонПериода, ДЕНЬ) КАК ЧислоДнейХранения,
    ТоварыНаСкладах.Регистратор.ДокументОснование КАК ДокументОснование,
    ТоварыНаСкладах.Регистратор.Партнер.ОсновнойМенеджер КАК ОсновнойМенеджер
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаОстатки КАК ТаблицаОстатки
        ПО ТоварыНаСкладах.Склад = ТаблицаОстатки.Склад
            И ТоварыНаСкладах.Номенклатура = ТаблицаОстатки.Номенклатура
            И ТоварыНаСкладах.Характеристика = ТаблицаОстатки.Характеристика
ГДЕ
    ТоварыНаСкладах.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
    И ТоварыНаСкладах.Период <= &КонПериода
СГРУППИРОВАТЬ ПО
    ТоварыНаСкладах.Склад,
    ТоварыНаСкладах.Номенклатура,
    ТоварыНаСкладах.Характеристика,
    НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ),
    ТоварыНаСкладах.Регистратор,
    РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ), &КонПериода, ДЕНЬ),
    ТоварыНаСкладах.Регистратор.ДокументОснование,
    ТоварыНаСкладах.Регистратор.Партнер.ОсновнойМенеджер
УПОРЯДОЧИТЬ ПО
    Период,
    Номенклатура,
    ХарактеристикаНоменклатуры
;

//////////////////////////////////////////////////////////////

ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ТоварыНаСкладах.Склад,
    ТоварыНаСкладах.Номенклатура КАК Номенклатура,
    ТоварыНаСкладах.Характеристика КАК ХарактеристикаНоменклатуры,
    СУММА(ТоварыНаСкладах.ВНаличии) КАК Количество,
    НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ) КАК Период,
    ТоварыНаСкладах.Регистратор,
    МАКСИМУМ(РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ), &КонПериода, ДЕНЬ)) КАК ЧислоДнейХранения
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаОстатки КАК ТаблицаОстатки
        ПО ТоварыНаСкладах.Склад = ТаблицаОстатки.Склад
            И ТоварыНаСкладах.Номенклатура = ТаблицаОстатки.Номенклатура
            И ТоварыНаСкладах.Характеристика = ТаблицаОстатки.Характеристика
ГДЕ
    ТоварыНаСкладах.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
    И ТоварыНаСкладах.Период <= &КонПериода
СГРУППИРОВАТЬ ПО
    ТоварыНаСкладах.Склад,
    ТоварыНаСкладах.Номенклатура,
    ТоварыНаСкладах.Характеристика,
    НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ),
    ТоварыНаСкладах.Регистратор

Почему этот запрос не дает желаемого результата? Проблема заключается в том, что регистр ТоварыНаСкладах отслеживает лишь факт прихода или расхода товара в целом. Он не позволяет однозначно определить, какая именно "партия" товара (т.е. товар, пришедший по конкретному документу в определенную дату) сейчас находится на остатке. Если товар приходил несколькими партиями и затем частично выбывал, этот регистр не сохраняет "возраст" оставшихся единиц товара. Например, если 01.01 пришло 10 шт., 02.01 еще 20 шт., а 02.02 выбыло 20 шт., мы не можем просто по движениям сказать, что именно осталось и с какой даты лежит. Нам нужна информация о партиях.

Основа решения: Партионный учет

Для корректного определения срока хранения товаров нам необходимо использовать **партионный учет**. Это означает, что мы должны отслеживать каждую группу товаров (партию), поступившую по определенному документу, с конкретной датой. Именно дата прихода такой партии является отправной точкой для расчета срока ее хранения на складе.

Коллеги в обсуждении верно указали на этот ключевой аспект: "Нужны остатки по партиям товаров", "Тут без партионного учета не обойтись", "Надо получить остатки по партиям, потом анализировать даты партий".

УТ11 не имеет прямого регистра ПартииТоваровНаСкладах, что иногда вызывает затруднения. Однако, функциональность партионного учета для целей определения себестоимости и, соответственно, отслеживания "возраста" партий, реализована в других регистрах. Мы разберем, какие регистры нам помогут.

Идентифицируем необходимые регистры в УТ11 для партионного учета

Для того чтобы определить срок хранения товара, нам потребуются регистры, которые хранят информацию о поступлении товаров в разрезе партий. В УТ11 это, как правило:

  1. Регистр накопления СебестоимостьТоваров. Этот регистр является основным для учета себестоимости и содержит информацию о партиях товаров в разрезе аналитик, включая документ поступления (партию). Каждая запись в этом регистре, относящаяся к приходу, содержит ссылку на документ-регистратор, который является по сути "партией".
  2. Регистр накопления ПартииТоваровОрганизаций. Хотя его название может сбивать с толку, и он используется преимущественно для управленческого учета партий, в зависимости от версии и настроек УТ11 он также может быть полезен для отслеживания дат поступления партий.

Важно понимать, что для решения нашей задачи нам нужна дата документа, который сформировал приход конкретной партии. Именно этот документ (например, ПриобретениеТоваровУслуг, ПоступлениеТоваровОтПереработчика, ПрочиеПриходыТоваров и т.д.) будет выступать в качестве идентификатора партии и источника даты ее поступления.

Разрабатываем запрос по шагам для определения срока хранения

Мы предлагаем следующий алгоритм построения запроса, который позволит нам получить желаемый результат:

Шаг 1: Получаем текущие остатки товаров на складе

Сначала нам необходимо получить актуальные остатки товаров на выбранном складе. Для этого используем виртуальную таблицу Остатки регистра накопления ТоварыНаСкладах. Мы сгруппируем данные по складу, номенклатуре и характеристике.


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ОстаткиТоваров.Склад,
    ОстаткиТоваров.Номенклатура,
    ОстаткиТоваров.Характеристика,
    ОстаткиТоваров.ВНаличииОстаток КАК КоличествоНаСкладе
ПОМЕСТИТЬ ВТ_ТекущиеОстатки
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(
        &КонПериода,
        (Склад В ИЕРАРХИИ (&Склад) ИЛИ &Склад = ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка))
        И (Номенклатура В ИЕРАРХИИ (&Номенклатура) ИЛИ &Номенклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка))
    ) КАК ОстаткиТоваров
;

В этом временном запросе мы получаем список товаров, которые на дату &КонПериода числятся на складе, с учетом возможных фильтров по складу и номенклатуре. Параметры &КонПериода, &Склад, &Номенклатура будут передаваться в запрос.

Шаг 2: Получаем информацию о дате поступления партий

Теперь мы переходим к самому важному шагу — получению даты, когда конкретная партия товара поступила в организацию. Для этого мы обратимся к регистру СебестоимостьТоваров. Этот регистр хранит данные о партиях, включая ссылку на документ-регистратор, который сформировал приход этой партии.


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ДвиженияСебестоимости.Номенклатура,
    ДвиженияСебестоимости.Характеристика,
    ДвиженияСебестоимости.Склад,
    ДвиженияСебестоимости.ДокументОприходования КАК Партия,
    НАЧАЛОПЕРИОДА(ДвиженияСебестоимости.Период, ДЕНЬ) КАК ДатаПоступленияПартии,
    СУММА(ДвиженияСебестоимости.КоличествоПриход) КАК КоличествоПриходПартии
ПОМЕСТИТЬ ВТ_ПартииПрихода
ИЗ
    РегистрНакопления.СебестоимостьТоваров КАК ДвиженияСебестоимости
ГДЕ
    ДвиженияСебестоимости.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
    И ДвиженияСебестоимости.Период <= &КонПериода
СГРУППИРОВАТЬ ПО
    ДвиженияСебестоимости.Номенклатура,
    ДвиженияСебестоимости.Характеристика,
    ДвиженияСебестоимости.Склад,
    ДвиженияСебестоимости.ДокументОприходования,
    НАЧАЛОПЕРИОДА(ДвиженияСебестоимости.Период, ДЕНЬ)
ИМЕЮЩИЕ
    СУММА(ДвиженияСебестоимости.КоличествоПриход) > 0
;

Мы выбираем только приходные движения (ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)) до указанной даты. Поле ДокументОприходования является ключевым, так как именно оно идентифицирует конкретную партию. Дата поступления партии берется из поля Период регистра, приведенного к началу дня с помощью НАЧАЛОПЕРИОДА(..., ДЕНЬ). Мы также суммируем количество, пришедшее по этой партии, чтобы иметь представление о размере партии.

Шаг 3: Объединяем данные и рассчитываем срок хранения

Теперь, когда у нас есть текущие остатки и информация о приходах партий, мы можем объединить эти данные. Мы используем ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) между нашими текущими остатками и таблицей партий, чтобы сохранить все остатки, даже если для них не найдется соответствующей партии (хотя в корректно настроенной системе такое встречается редко).

Для каждого элемента номенклатуры мы хотим найти самый старый документ прихода, который до сих пор имеет остаток. Это сложная задача, так как списание могло происходить по методу FIFO, LIFO или по средней. Если нам нужно найти *физически* старые товары, а не просто *учетные* партии, то это требует гораздо более сложного алгоритма. Однако, для большинства управленческих задач достаточно определить "возраст" **учетной** партии.

В нашем случае, когда спрашивается "сколько товар лежит на складе", подразумевается возраст *учетной* партии, которая теоретически "лежит". Мы будем исходить из того, что нам достаточно определить, какие партии, пришедшие более 150 дней назад, еще имеют остаток на складе.


ВЫБРАТЬ
    Остатки.Склад,
    Остатки.Номенклатура,
    Остатки.Характеристика,
    Остатки.КоличествоНаСкладе,
    Партии.Партия,
    Партии.ДатаПоступленияПартии,
    РАЗНОСТЬДАТ(Партии.ДатаПоступленияПартии, &КонПериода, ДЕНЬ) КАК ЧислоДнейХранения
ПОМЕСТИТЬ ВТ_Соединение
ИЗ
    ВТ_ТекущиеОстатки КАК Остатки
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ПартииПрихода КАК Партии
        ПО Остатки.Номенклатура = Партии.Номенклатура
            И Остатки.Характеристика = Партии.Характеристика
            И Остатки.Склад = Партии.Склад
ГДЕ
    Партии.ДатаПоступленияПартии ЕСТЬ НЕ NULL
;

На этом этапе мы получили информацию о текущих остатках и, для каждой строки, нашли соответствующую партию с датой ее поступления. Мы также рассчитали ЧислоДнейХранения, используя функцию РАЗНОСТЬДАТ. Эта функция позволяет нам получить разницу между двумя датами в указанных единицах (в данном случае — ДЕНЬ). Синтаксис РАЗНОСТЬДАТ(ДатаНачала, ДатаОкончания, ЕдиницаИзмерения).

Шаг 4: Отфильтровываем товары, хранящиеся дольше заданного срока, и агрегируем результат

Наконец, мы выбираем только те товары, у которых ЧислоДнейХранения превышает 150 дней, и агрегируем их, чтобы получить общее количество залежавшейся номенклатуры.


ВЫБРАТЬ
    ФинальныйРезультат.Склад,
    ФинальныйРезультат.Номенклатура,
    ФинальныйРезультат.Характеристика,
    СУММА(ФинальныйРезультат.КоличествоНаСкладе) КАК КоличествоЗалежавшегосяТовара,
    МАКСИМУМ(ФинальныйРезультат.ЧислоДнейХранения) КАК МаксимальноеЧислоДнейХранения
ИЗ
    ВТ_Соединение КАК ФинальныйРезультат
ГДЕ
    ФинальныйРезультат.ЧислоДнейХранения > 150
СГРУППИРОВАТЬ ПО
    ФинальныйРезультат.Склад,
    ФинальныйРезультат.Номенклатура,
    ФинальныйРезультат.Характеристика
УПОРЯДОЧИТЬ ПО
    ФинальныйРезультат.Склад,
    ФинальныйРезультат.Номенклатура
;

В этом заключительном запросе мы производим фильтрацию по нашему условию (больше 150 дней). Используем функцию СУММА() для получения общего количества залежавшегося товара по каждой номенклатуре и МАКСИМУМ() для вывода максимального срока хранения (если вдруг одна и та же номенклатура попадает в отчет с нескольких партий разного возраста, хотя чаще всего нас интересует именно самая старая партия).

Полный текст запроса

Объединим все временные таблицы и финальный запрос в один, который вы можете использовать. Для удобства мы можем сразу же включить расчет дней хранения в общую структуру.


// ВТ_ТекущиеОстатки: Получаем текущие остатки товаров
ВЫБРАТЬ
    ОстаткиТоваров.Склад КАК Склад,
    ОстаткиТоваров.Номенклатура КАК Номенклатура,
    ОстаткиТоваров.Характеристика КАК Характеристика,
    ОстаткиТоваров.ВНаличииОстаток КАК КоличествоНаСкладе
ПОМЕСТИТЬ ВТ_ТекущиеОстатки
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(
        &КонПериода,
        (Склад В ИЕРАРХИИ (&Склад) ИЛИ &Склад = ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка))
        И (Номенклатура В ИЕРАРХИИ (&Номенклатура) ИЛИ &Номенклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка))
    ) КАК ОстаткиТоваров
;

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

// Основной запрос: Собираем информацию, рассчитываем дни хранения и фильтруем
ВЫБРАТЬ РАЗРЕШЕННЫЕ
    Партии.Склад,
    Партии.Номенклатура,
    Партии.Характеристика,
    СУММА(Партии.КоличествоНаСкладе) КАК КоличествоЗалежавшегосяТовара,
    МАКСИМУМ(РАЗНОСТЬДАТ(Партии.ДатаПоступленияПартии, &КонПериода, ДЕНЬ)) КАК ЧислоДнейХранения
ИЗ
    ВТ_ПартииПриходаДляОстатков КАК Партии
ГДЕ
    РАЗНОСТЬДАТ(Партии.ДатаПоступленияПартии, &КонПериода, ДЕНЬ) > 150
СГРУППИРОВАТЬ ПО
    Партии.Склад,
    Партии.Номенклатура,
    Партии.Характеристика
УПОРЯДОЧИТЬ ПО
    Склад,
    Номенклатура
;

Обратите внимание, что мы использовали ВНУТРЕННЕЕ СОЕДИНЕНИЕ во второй временной таблице. Это означает, что мы будем рассматривать только те остатки, для которых найдена соответствующая приходная партия в регистре СебестоимостьТоваров. Если по какой-то причине остаток есть, но его приход не зафиксирован в регистре себестоимости (например, из-за некорректных данных), такой товар не попадет в отчет, что, в общем случае, является правильным поведению, так как мы не можем определить его "возраст".

Важные соображения и лучшие практики

  1. Метод учета себестоимости: В УТ11 метод учета себестоимости (FIFO, LIFO, по средней, скользящая средняя) влияет на то, какие партии считаются "выбывшими". Наш запрос определяет "возраст" **приходной учетной партии**, а не физической единицы товара. Если в организации используется метод FIFO (первым пришел – первым ушел), то товары, пришедшие раньше, считаются выбывшими первыми. В таком случае наш подход позволяет достаточно точно определить "возраст" оставшихся товаров. Если же используется LIFO или по средней, то "возраст" оставшейся партии может не соответствовать ее реальному физическому нахождению на складе, но для управленческих целей такой подход обычно приемлем.
  2. Учет по характеристикам и сериям: Если в вашей конфигурации активно используется учет по характеристикам или сериям номенклатуры, убедитесь, что эти поля включены в условия соединения (ПО) и группировки (СГРУППИРОВАТЬ ПО), чтобы детализация была максимально точной.
  3. "Брак" и специфические товары: В сообщении 9 автор упоминает необходимость отслеживания "брака". Если в системе нет явного признака "брака" в номенклатуре или характеристике, наш запрос найдет просто любой товар, который залежался. Если же "брак" маркируется отдельной характеристикой или свойством, мы можем добавить соответствующее условие в запрос (например, И Партии.Характеристика.Наименование = "Брак").
  4. Параметры запроса: Для удобства и гибкости, в запросе используются параметры:
    • &КонПериода: Дата, на которую формируется отчет (обычно текущая дата).
    • &Склад: Склад, по которому требуется получить информацию (может быть пустой ссылкой для всех складов).
    • &Номенклатура: Номенклатура или группа номенклатуры для фильтрации (может быть пустой ссылкой для всей номенклатуры).
    Мы должны установить эти параметры перед выполнением запроса.
  5. Оптимизация запроса: Для очень больших баз данных с миллионами движений регистров, запросы с несколькими временными таблицами могут выполняться долго. В таких случаях можно рассмотреть индексирование полей, участвующих в соединении, или создание более сложных отчетов на базе системы компоновки данных (СКД), которая иногда может предложить более оптимальный план запроса.

Заключение

Используя представленный подход с обращением к регистру накопления СебестоимостьТоваров (или ПартииТоваровОрганизаций в зависимости от специфики учета) и грамотным соединением данных по партиям и текущим остаткам, мы можем эффективно выявлять залежавшиеся товары на складе в 1С:Управление торговлей 11. Этот метод позволяет получить точную информацию о "возрасте" товарных партий, что является критически важным для принятия управленческих решений по оптимизации складских запасов, предотвращению убытков от порчи и снижению затрат на хранение.

Решение этой задачи требует понимания принципов партионного учета и структуры хранения данных в УТ11, но его реализация значительно повышает эффективность управления товарными запасами.

← На главную