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

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

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

Разберем методику и структуру запроса в 1С, позволяющего корректно учесть суточные остатки, внешние обороты и рассчитать сумму к оплате за услуги хранения.

Анализ ключевых требований и особенностей учета

Для корректного биллинга хранения и анализа сроков нахождения номенклатуры на складе необходимо учесть несколько правил:

  1. Учет однодневного хранения: если товар поступил на склад и был отгружен в тот же день, этот день должен тарифицироваться как 1 день хранения.
  2. Исключение внутренних перемещений: перемещение между складами одной организации не является фактом выбытия или нового поступления с точки зрения биллинга клиента. Анализ должен выполняться в разрезе компании/организации, а не физических складов.
  3. Учет переходящих остатков: партии, поступившие до начала расчетного периода и продолжающие лежать на складе, должны оплачиваться за каждый день расчетного месяца.
  4. Разная длительность месяцев: если тариф на хранение установлен за календарный месяц, расчет стоимости суток должен динамически делить месячную ставку на фактическое число дней в месяце.

Математическая модель расчета товаро-дней (храно-дней)

Рассмотрим, как зафиксировать факт хранения единицы товара в конкретные сутки. При подневном анализе стандартной таблицы остатков и оборотов день с однодневным оборотом (приход и расход в один день) покажет нулевой начальный и нулевой конечный остатки, что приведет к потере данных о хранении.

Чтобы этого избежать, объем хранимого товара в течение суток рассчитывается по следующему принципу: к начальному остатку на дату прибавляется объем внешнего прихода за этот же день (либо берется формула КонечныйОстаток + Расход).

Проанализируем пример движения товара по дням:

  1. 01.12: НачОст = 2, Приход = 3, Расход = 0, КонОст = 5. Хранилось: 2 + 3 = 5 шт.
  2. 02.12: НачОст = 5, Приход = 0, Расход = 5, КонОст = 0. Хранилось: 5 + 0 = 5 шт. (списание в конце дня, факт хранения зафиксирован).
  3. 03.12: НачОст = 0, Приход = 0, Расход = 0, КонОст = 0. Хранилось: 0 шт.
  4. 04.12: НачОст = 0, Приход = 4, Расход = 0, КонОст = 4. Хранилось: 0 + 4 = 4 шт.

Построение запроса для биллинга хранения

Разберем по шагам текст запроса, который формирует подневную сетку, сопоставляет движения по регистру ПартииТоваровНаСкладах и рассчитывает итоговую сумму с учетом тарифной сетки из регистра сведений.


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

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

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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ОстаткиНаНачало.ДатаКалендаря КАК ДатаКалендаря,
    ВТ_ОстаткиНаНачало.Номенклатура КАК Номенклатура,
    ВТ_ОстаткиНаНачало.СерияНоменклатуры КАК СерияНоменклатуры,
    ВТ_ОстаткиНаНачало.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
    ВТ_ОстаткиНаНачало.КонечныйОстаток - ЕСТЬNULL(ВТ_Обороты.Количество, 0) + ВЫБОР
        КОГДА ЕСТЬNULL(ВТ_Обороты.Количество, 0) > 0
            ТОГДА ЕСТЬNULL(ВТ_Обороты.Количество, 0)
        ИНАЧЕ 0
    КОНЕЦ КАК ХранилосьНаСкладе,
    (ВТ_ОстаткиНаНачало.КонечныйОстаток - ЕСТЬNULL(ВТ_Обороты.Количество, 0) + ВЫБОР
        КОГДА ЕСТЬNULL(ВТ_Обороты.Количество, 0) > 0
            ТОГДА ЕСТЬNULL(ВТ_Обороты.Количество, 0)
        ИНАЧЕ 0
    КОНЕЦ) * (ВТ_РасценкиНаХранение.Цена / ДЕНЬ(КОНЕЦПЕРИОДА(ВТ_ОстаткиНаНачало.ДатаКалендаря, МЕСЯЦ))) КАК КОплатеЗаДень
ИЗ
    ВТ_ОстаткиНаНачало КАК ВТ_ОстаткиНаНачало
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Обороты КАК ВТ_Обороты
        ПО ВТ_ОстаткиНаНачало.ДатаКалендаря = ВТ_Обороты.Период
        И ВТ_ОстаткиНаНачало.Номенклатура = ВТ_Обороты.Номенклатура
        И ВТ_ОстаткиНаНачало.СерияНоменклатуры = ВТ_Обороты.СерияНоменклатуры
        И ВТ_ОстаткиНаНачало.ХарактеристикаНоменклатуры = ВТ_Обороты.ХарактеристикаНоменклатуры
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_РасценкиНаХранение КАК ВТ_РасценкиНаХранение
        ПО ВТ_ОстаткиНаНачало.Номенклатура = ВТ_РасценкиНаХранение.Номенклатура
        И ВТ_ОстаткиНаНачало.ДатаКалендаря = ВТ_РасценкиНаХранение.ДатаКалендаря
УПОРЯДОЧИТЬ ПО
    Номенклатура,
    СерияНоменклатуры,
    ХарактеристикаНоменклатуры,
    ДатаКалендаря

Оптимизация и практические рекомендации

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

  1. Зависимость от производственного календаря: В запросе используется РегистрСведений.РегламентированныйПроизводственныйКалендарь. Если в информационной базе календарь заполнен не на все будущие или прошлые периоды, запрос вернет неполные данные. Рекомендуется перед выполнением запроса передавать массив дат за требуемый период через ТаблицаЗначений в менеджер временных таблиц.
  2. Исключение внутренних движений: В таблице ВТ_Обороты установлен фильтр НЕ Обороты.КодОперации В (&СписокОпераций). В параметр &СписокОпераций необходимо передать значение перечисления КодыОперацийПартииТоваров.Перемещение (или аналогичные внутренние операции), чтобы исключить задвоение оборотов при перемещении между складами.
  3. Партионный учет в УТ 10.3: Следует помнить, что в типовой конфигурации движения по регистру ПартииТоваровНаСкладах могут формироваться с задержкой, если списание партий выполняется регламентной обработкой Проведение по партиям. Перед формированием биллинга убедитесь, что партионный учет восстановлен на конец анализируемого периода.
  4. Альтернатива на СКД: Если отчет строится с использованием Системы компоновки данных, необходимость в соединении с календарем отпадает. Достаточно включить флаг Дополнять периоды в настройках группировки по полю ПериодДень, указав границы дат отчета. СКД автоматически сгенерирует недостающие дни без просадки производительности СУБД.
← На главную