Как в запросе 1С привязать дополнительные расходы к себестоимости прихода за предыдущую дату?

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

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

Анализ проблемы и постановка задачи

Основная сложность заключается в том, что в стандартном ЛЕВОЕ СОЕДИНЕНИЕ по условию Период = Период мы получим данные только за те дни, когда оба события совпали. Если в день начисления доп. расхода прихода не было, соединение вернет NULL. Нам же необходимо реализовать алгоритм «поиска последнего известного значения», когда для каждой записи о расходах система находит в прошлом ближайший по времени документ прихода для конкретного товара.

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

Способ 1. Реализация через временные таблицы и поиск максимальной даты

Этот метод является наиболее надежным и производительным для обычных запросов. Разберем его по шагам, используя временные таблицы для оптимизации работы СУБД.

Шаг 1. Подготовка данных о расходах. Сначала выберем все операции доп. расходов и поместим их во временную таблицу.


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

Шаг 2. Поиск ближайшей даты прихода. Теперь для каждого товара и даты расхода нам нужно найти дату последнего предшествующего прихода. Для этого мы соединяем таблицу расходов с таблицей приходов по условию Приход.Период <= Расход.Период и выбираем максимум.


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

Шаг 3. Сбор итоговых данных. На финальном этапе мы соединяем наши расходы с таблицей дат, а затем — с данными о стоимости прихода за эту найденную дату.


ВЫБРАТЬ
    ВТ_ДопРасходы.ДеньРасхода КАК Период,
    ВТ_ДопРасходы.Товар КАК Товар,
    ВТ_ДопРасходы.СуммаДопРасходов КАК ДопРасходы,
    ЕСТЬNULL(ДанныеПрихода.СебестоимостьПриход, 0) КАК СебестоимостьПрихода,
    ВЫБОР 
        КОГДА ЕСТЬNULL(ДанныеПрихода.СебестоимостьПриход, 0) > 0 
        ТОГДА (ВТ_ДопРасходы.СуммаДопРасходов / ДанныеПрихода.СебестоимостьПриход) * 100 
        ИНАЧЕ 0 
    КОНЕЦ КАК ПроцентДопРасходов
ИЗ
    ВТ_ДопРасходы КАК ВТ_ДопРасходы
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СвязьДам КАК ВТ_СвязьДам
        ПО ВТ_ДопРасходы.Товар = ВТ_СвязьДам.Товар
        И ВТ_ДопРасходы.ДеньРасхода = ВТ_СвязьДам.ДеньРасхода
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СебестоимостьТоваров.Обороты(, &КонецПериода, День, ) КАК ДанныеПрихода
        ПО ВТ_СвязьДам.Товар = ДанныеПрихода.Товар
        И ВТ_СвязьДам.ДатаПоследнегоПрихода = ДанныеПрихода.Период;

Способ 2. Использование механизмов СКД (Связи наборов данных)

Проанализируем альтернативный вариант, который часто оказывается проще в реализации для отчетов. В СКД можно создать два отдельных набора данных-запросов и настроить связь между ними.

  1. Набор данных 1 (ДопРасходы): Запрос, который просто выбирает обороты дополнительных расходов по дням.
  2. Набор данных 2 (СебестоимостьПрихода): Запрос к виртуальной таблице ОстаткиИОбороты или Обороты, который будет возвращать данные о приходе. В параметрах виртуальной таблицы этого набора мы можем использовать условие на период.

Перейдем на закладку Связи наборов данных и настроим их следующим образом:

Этот метод работает аналогично СрезуПоследних. Чтобы получить именно последний приход, во втором наборе данных можно использовать конструкцию ВЫБРАТЬ ПЕРВЫЕ 1 ... УПОРЯДОЧИТЬ ПО Период УБЫВ.

Оптимизация и важные нюансы

Рассмотрим несколько критических моментов, которые помогут избежать ошибок при расчете себестоимости:

1. Использование индексов. При работе с большими объемами данных во временных таблицах (ВТ_ДопРасходы) обязательно создавайте индексы по полям Товар и ДеньРасхода. Это позволит СУБД использовать эффективные алгоритмы поиска при соединении таблиц.

2. Обработка пустых значений. Проанализируем ситуацию, когда прихода по товару не было вовсе (например, ввод остатков). В этом случае ЕСТЬNULL в итоговом запросе предотвратит появление пустых ячеек в отчете. Также крайне важно проверять делитель на ноль перед расчетом процента, используя конструкцию ВЫБОР КОГДА.

3. Периодичность. Если доп. расходы и приходы могут происходить в течение одного дня несколькими документами, лучше использовать периодичность Регистратор вместо День. Это позволит более точно сопоставить расходы с конкретными поставками, если это предусмотрено логикой учета.

4. Партионный учет. Выясним, включен ли в программе партионный учет. Если да, то в регистре СебестоимостьТоваров заполняется измерение АналитикаУчетаНоменклатуры или Партия. В этом случае задача упрощается: доп. расходы можно соединять напрямую по полю партии, что даст стопроцентную точность без поиска по датам.

Выбор конкретного метода зависит от требований к производительности и сложности структуры вашего отчета. Для простых отчетов на СКД идеально подходит связывание наборов данных, в то время как для сложных расчетных механизмов лучше использовать классический запрос на временных таблицах с поиском максимального периода.

← На главную