← Назад к вопросам

Вычисление количества дней товаров на складе

1.8 Middle🔥 131 комментариев
#Запросы и оптимизация

Условие

Напишите запрос или обработку для вычисления количества дней нахождения товаров на складе.

Требования:

  • Для каждого товара определить дату последнего поступления
  • Рассчитать количество дней от поступления до текущей даты
  • Выделить товары, которые лежат на складе более 90 дней

Пример результата

ТоварДата поступленияДней на складеСтатус
Товар 101.09.2024120Залежалый
Товар 215.11.202445Норма
Товар 301.10.202490Критично

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Вычисление дней товаров на складе

Решение с использованием регистра движения товаров

Функция ПолучитьДолгоЛежащиеТовары(ДатаОтчета, Склад = Неопределено)
    
    Запрос = Новый Запрос();
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    МахДокумент.Товар,
    |    Номенклатура.Наименование КАК ТоварНаименование,
    |    МахДокумент.ДатаПоступления,
    |    РАЗНОСТЬДАТ(МахДокумент.ДатаПоступления, &ДатаОтчета, ДЕНЬ) КАК ДнейНаСкладе,
    |    СЛУЧАЙ
    |        КОГДА РАЗНОСТЬДАТ(МахДокумент.ДатаПоступления, &ДатаОтчета, ДЕНЬ) > 90
    |            ТОГДА \"Залежалый\"
    |        КОГДА РАЗНОСТЬДАТ(МахДокумент.ДатаПоступления, &ДатаОтчета, ДЕНЬ) = 90
    |            ТОГДА \"Критично\"
    |        ИНАЧЕ \"Норма\"
    |    КОНЕЦ КАК Статус
    |ИЗ
    |    (ВЫБРАТЬ
    |        ДвижениеТоваров.Товар,
    |        МАКСИМУМ(ДвижениеТоваров.Период) КАК ДатаПоступления
    |    ИЗ РегистрНакопления.ДвижениеТоваров КАК ДвижениеТоваров
    |    ГДЕ ДвижениеТоваров.ВидДвижения = ЗНАЧЕНИЕ(Перечисления.ВидДвижения.Поступление)";
    
    Если ЗначениеЗаполнено(Склад) Тогда
        Запрос.Текст = Запрос.Текст + " И ДвижениеТоваров.Склад = &Склад";
    КонецЕсли;
    
    Запрос.Текст = Запрос.Текст + 
    "    ГРУППИРОВАТЬ ПО
    |        ДвижениеТоваров.Товар
    |    ) КАК МахДокумент
    |    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
    |        ПО МахДокумент.Товар = Номенклатура.Ссылка
    |ГДЕ РАЗНОСТЬДАТ(МахДокумент.ДатаПоступления, &ДатаОтчета, ДЕНЬ) >= 90
    |УПОРЯДОЧИТЬ ПО
    |    ДнейНаСкладе УБЫВ";
    
    Запрос.УстановитьПараметр("ДатаОтчета", ДатаОтчета);
    
    Если ЗначениеЗаполнено(Склад) Тогда
        Запрос.УстановитьПараметр("Склад", Склад);
    КонецЕсли;
    
    Результат = Запрос.Выполнить();
    Возврат Результат.Выгрузить();
    
КонецФункции

Ключевые функции:

  • РАЗНОСТЬДАТ() — вычисляет разницу между датами в днях
  • МАКСИМУМ() — находит последнюю дату поступления
  • СЛУЧАЙ/КОГДА — определяет статус товара
  • Подзапрос находит последнее поступление для каждого товара

Более точный вариант с реальными документами поступления

Функция ПолучитьТоварыСоДняхНаСкладе(ДатаОтчета, Склад = Неопределено)
    
    Запрос = Новый Запрос();
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    ПриходНоменклатура.Номенклатура,
    |    Номенклатура.Наименование,
    |    МАКСИМУМ(ПриходНоменклатура.ДатаДокумента) КАК ДатаПоступления,
    |    РАЗНОСТЬДАТ(МАКСИМУМ(ПриходНоменклатура.ДатаДокумента), &ДатаОтчета, ДЕНЬ) КАК ДнейНаСкладе,
    |    СУММА(ПриходНоменклатура.Количество) КАК КоличествоНаСкладе,
    |    СЛУЧАЙ
    |        КОГДА РАЗНОСТЬДАТ(МАКСИМУМ(ПриходНоменклатура.ДатаДокумента), &ДатаОтчета, ДЕНЬ) > 90
    |            ТОГДА \"Залежалый\"
    |        КОГДА РАЗНОСТЬДАТ(МАКСИМУМ(ПриходНоменклатура.ДатаДокумента), &ДатаОтчета, ДЕНЬ) = 90
    |            ТОГДА \"Критично\"
    |        ИНАЧЕ \"Норма\"
    |    КОНЕЦ КАК Статус
    |ИЗ Документ.ПриходТовара.Товары КАК ПриходНоменклатура
    |    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
    |        ПО ПриходНоменклатура.Номенклатура = Номенклатура.Ссылка
    |    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПриходТовара КАК ПриходДокумент
    |        ПО ПриходНоменклатура.Ссылка = ПриходДокумент.Ссылка
    |        И ПриходДокумент.Проведен = ИСТИНА
    |ГДЕ ПриходНоменклатура.ДатаДокумента <= &ДатаОтчета";
    
    Если ЗначениеЗаполнено(Склад) Тогда
        Запрос.Текст = Запрос.Текст + " И ПриходДокумент.Склад = &Склад";
    КонецЕсли;
    
    Запрос.Текст = Запрос.Текст + 
    "    ГРУППИРОВАТЬ ПО
    |        ПриходНоменклатура.Номенклатура,
    |        Номенклатура.Наименование
    |    ИМЕЮЩИЕ РАЗНОСТЬДАТ(МАКСИМУМ(ПриходНоменклатура.ДатаДокумента), &ДатаОтчета, ДЕНЬ) >= 90
    |    УПОРЯДОЧИТЬ ПО
    |        ДнейНаСкладе УБЫВ";
    
    Запрос.УстановитьПараметр("ДатаОтчета", ДатаОтчета);
    
    Если ЗначениеЗаполнено(Склад) Тогда
        Запрос.УстановитьПараметр("Склад", Склад);
    КонецЕсли;
    
    Результат = Запрос.Выполнить();
    Возврат Результат.Выгрузить();
    
КонецФункции

Вариант с учётом остатков (более точный)

Функция ПолучитьТоварыСОстаткамиНаСкладе(ДатаОтчета, Склад)
    
    // Получаем остатки товаров на складе
    Запрос = Новый Запрос();
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    ОстаткиИОбороты.Товар,
    |    ОстаткиИОбороты.Номенклатура,
    |    ОстаткиИОбороты.ОстатокНаКонецПериода КАК ОстатокТовара,
    |    МАКСИМУМ(ПриходНоменклатура.ДатаДокумента) КАК ДатаПоследнегоПоступления,
    |    РАЗНОСТЬДАТ(МАКСИМУМ(ПриходНоменклатура.ДатаДокумента), &ДатаОтчета, ДЕНЬ) КАК ДнейНаСкладе
    |ИЗ
    |    (ВЫБРАТЬ
    |        ДвижениеТоваров.Товар,
    |        Номенклатура.Наименование КАК Номенклатура,
    |        СУММА(СЛУЧАЙ
    |            КОГДА ДвижениеТоваров.ВидДвижения = ЗНАЧЕНИЕ(Перечисления.ВидДвижения.Поступление)
    |                ТОГДА ДвижениеТоваров.КоличествоПриход
    |            ИНАЧЕ -ДвижениеТоваров.КоличествоРасход
    |        КОНЕЦ) КАК ОстатокНаКонецПериода
    |    ИЗ РегистрНакопления.ДвижениеТоваров КАК ДвижениеТоваров
    |        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
    |            ПО ДвижениеТоваров.Товар = Номенклатура.Ссылка
    |    ГДЕ ДвижениеТоваров.Склад = &Склад
    |        И ДвижениеТоваров.Период <= &ДатаОтчета
    |    ГРУППИРОВАТЬ ПО
    |        ДвижениеТоваров.Товар,
    |        Номенклатура.Наименование
    |    ИМЕЮЩИЕ СУММА(СЛУЧАЙ
    |            КОГДА ДвижениеТоваров.ВидДвижения = ЗНАЧЕНИЕ(Перечисления.ВидДвижения.Поступление)
    |                ТОГДА ДвижениеТоваров.КоличествоПриход
    |            ИНАЧЕ -ДвижениеТоваров.КоличествоРасход
    |        КОНЕЦ) > 0
    |    ) КАК ОстаткиИОбороты
    |    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриходТовара.Товары КАК ПриходНоменклатура
    |        ПО ОстаткиИОбороты.Товар = ПриходНоменклатура.Номенклатура
    |        И ПриходНоменклатура.ДатаДокумента <= &ДатаОтчета
    |ГРУППИРОВАТЬ ПО
    |    ОстаткиИОбороты.Товар,
    |    ОстаткиИОбороты.Номенклатура,
    |    ОстаткиИОбороты.ОстатокНаКонецПериода
    |ИМЕЮЩИЕ
    |    ОстаткиИОбороты.ОстатокНаКонецПериода > 0
    |    И РАЗНОСТЬДАТ(МАКСИМУМ(ПриходНоменклатура.ДатаДокумента), &ДатаОтчета, ДЕНЬ) >= 90
    |УПОРЯДОЧИТЬ ПО
    |    ДнейНаСкладе УБЫВ";
    
    Запрос.УстановитьПараметр("ДатаОтчета", ДатаОтчета);
    Запрос.УстановитьПараметр("Склад", Склад);
    
    Результат = Запрос.Выполнить();
    Возврат Результат.Выгрузить();
    
КонецФункции

Обработка для анализа залежей

Процедура ПроанализироватьЗалежиТовара(ДатаОтчета, Склад)
    
    Таблица = ПолучитьТоварыСОстаткамиНаСкладе(ДатаОтчета, Склад);
    
    КритическиеТовары = Новый Массив();
    ЗалежалыеТовары = Новый Массив();
    
    Для Каждого Строка Из Таблица Цикл
        
        Если Строка.ДнейНаСкладе > 90 Тогда
            ЗалежалыеТовары.Добавить(Строка);
        ИначеЕсли Строка.ДнейНаСкладе = 90 Тогда
            КритическиеТовары.Добавить(Строка);
        КонецЕсли;
        
    КонецЦикла;
    
    СообщитьОСтатистике(ЗалежалыеТовары, КритическиеТовары);
    
КонецПроцедуры

Процедура СообщитьОСтатистике(ЗалежалыеТовары, КритическиеТовары)
    
    Текст = "Товары на складе свыше 90 дней: " + ЗалежалыеТовары.Количество() + ОК;
    
    Для Каждого Товар Из ЗалежалыеТовары Цикл
        Текст = Текст + Товар.Номенклатура + " - " + Товар.ДнейНаСкладе + " дней\n";
    КонецЦикла;
    
    Сообщить(Текст);
    
КонецПроцедуры

Оптимизация для больших объёмов

// Используем ВРЕМЕННУЮ ТАБЛИЦУ для кэширования результатов
Процедура РассчитатьИКэшироватьДанные(ДатаОтчета, Склад)
    
    Запрос = Новый Запрос();
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    ТекущаяДата() КАК ДатаРасчета,
    |    ДанныеТовара.Товар,
    |    ДанныеТовара.Наименование,
    |    ДанныеТовара.ДатаПоступления,
    |    ДанныеТовара.ДнейНаСкладе,
    |    ДанныеТовара.Статус
    |ПОМЕСТИТЬ ДолгоЛежащиеТовары
    |ИЗ (ВЫБРАТЬ ... ) КАК ДанныеТовара;
    
    """SELECT INTO таблица для быстрого доступа
    Запрос.Выполнить();
    
КонецПроцедуры

Ключевые моменты

  1. РАЗНОСТЬДАТ() — основная функция для расчёта дней
  2. МАКСИМУМ() — находит последнее поступление
  3. ИМЕЮЩИЕ — фильтрует по условиям после группировки
  4. ВНУТРЕННЕЕ СОЕДИНЕНИЕ — гарантирует наличие данных
  5. Кэширование — ускоряет работу при частых вызовах
Вычисление количества дней товаров на складе | PrepBro