Как рассчитать количество дней нахождения товара на складе в запросе 1С 7.7

Программист 1С v7.7 Управленческий учет Торговля и дистрибуция
← На главную

При построении аналитических отчетов в 1С 7.7 с использованием прямых запросов (SQL) часто возникает задача подсчитать, сколько дней конкретный товар фактически находился на складе за определенный период. Типичная ошибка в таких запросах приводит к тому, что дни, когда товар поступил и был списан в один и тот же день, выпадают из расчета (получается 0 дней), что искажает итоговую статистику. Разберем, как правильно составить запрос, чтобы избежать этой проблемы.

Анализ проблемы: почему стандартный подход выдает 0

Рассмотрим ситуацию, с которой столкнулся автор темы. В его запросе использовалась проверка только начального остатка товара на конкретную дату. Проанализируем фрагмент исходного кода:


CASE WHEN ВлЗапрос.НачОст > 0 THEN  
    -- ... логика расчета интервала ...
ELSE 
    CASE WHEN ВлЗапрос.КонОст > 0 THEN 
        DATEDIFF(day, ВлЗапрос.Период, convert(datetime, :КонДата, 112)) + 1 
    END
END

В чем заключается ошибка? Если товар поступил утром (НачальныйОстаток = 0) и был продан вечером того же дня (КонечныйОстаток = 0), то условие ВлЗапрос.НачОст > 0 вернет ложь. Программа не увидит, что в течение этого дня товар фактически присутствовал на складе. Выясним, как изменить это условие, чтобы охватить все движения товара.

Решение 1: Учет оборотов за день

Самый простой и эффективный способ исправить логику — анализировать не только остаток на начало дня, но и факт прихода товара. Если на начало дня товара не было, но в течение дня был приход (КоличествоПриход), этот день должен быть засчитан как день нахождения на складе.

Рассмотрим правильное условие для функции SUM. Вместо сложных вложенных конструкций CASE и DATEDIFF, если мы используем виртуальную таблицу $РегистрОстаткиОбороты с периодичностью День, нам достаточно проверить сумму начального остатка и прихода:


SELECT
    ВлЗапрос.Номенклатура,
    SUM(CASE WHEN (ВлЗапрос.НачОст + ВлЗапрос.Приход) > 0 THEN 1 ELSE 0 END) as ДнейНаСкладе
FROM (
    SELECT 
        Остатки.Номенклатура,
        Остатки.Период as Период,
        Остатки.КоличествоНачальныйОстаток НачОст,
        Остатки.КоличествоПриход Приход
    FROM $РегистрОстаткиОбороты.ОстаткиТМЦ(:НачДата, :КонДата~, День, ДвиженияИГраницыПериода, , ,) AS Остатки
) AS ВлЗапрос
GROUP BY ВлЗапрос.Номенклатура

В данном примере мы суммируем единицы за каждый день, когда условие (НачОст + Приход) > 0 выполняется. Это гарантирует, что даже если товар пришел и ушел в один день, сумма будет больше нуля, и день попадет в расчет.

Решение 2: Использование вспомогательного «календаря»

Важно понимать особенность работы виртуальной таблицы $РегистрОстаткиОбороты в 1С 7.7. Она возвращает строки только для тех дней, когда по товару были движения или на границы периода. Если товар лежал на складе 10 дней без движения, запрос может вернуть только одну строку на дату начала остатка, и простое суммирование строк SUM(1) выдаст результат 1 день вместо 10.

Для решения этой проблемы в SQL-запросах профессиональные разработчики используют соединение с таблицей дат (календарем). Разберем по шагам, как это работает:

  1. Создается или выбирается временная таблица, содержащая все даты отчетного периода.
  2. К этой таблице выполняется LEFT JOIN остатков товара.
  3. Для дней, где нет записей в регистре, остаток «протягивается» из предыдущих периодов.
  4. Считаются только те даты из календаря, где остаток или приход были больше нуля.

Проанализируем пример логики «протягивания» остатка без использования календаря, но с расчетом разницы дат, как пытался сделать автор:


-- Если товар есть на начало, считаем дни до следующего изменения или до конца периода
CASE 
    WHEN ВлЗапрос.НачОст > 0 THEN DATEDIFF(day, ВлЗапрос.Период, СледующаяДатаДвижения)
    WHEN ВлЗапрос.Приход > 0 THEN DATEDIFF(day, ВлЗапрос.Период, СледующаяДатаДвижения)
    ELSE 0 
END

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

Оптимизация параметров даты

Обратим внимание на технический нюанс, упомянутый в обсуждении. При передаче дат в запрос 1С++ часто используют конструкцию convert(datetime, :НачДата, 112). Посмотрим на более лаконичный и быстрый способ передачи параметров, который предлагает сама компонента 1С++:

Использование суффикса ~~ (две тильды) позволяет передать дату напрямую в формате SQL без лишних преобразований:

:НачДата~~ вместо convert(datetime, :НачДата, 112).

Это не только упрощает код, но и избавляет от потенциальных ошибок конвертации, связанных с региональными настройками сервера базы данных.

Рекомендации по реализации

Подведем итоги и сформируем алгоритм действий для корректного расчета:

Соблюдение этих правил позволит вам получать точные данные о складских запасах и корректно рассчитывать оборачиваемость товаров в вашей системе на базе 1С 7.7.

← На главную