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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов и обработок в среде 1С:Предприятие часто возникает классическая задача: при обходе строк табличной части документа необходимо получить данные из предыдущей строки. Ситуация существенно осложняется, если на выборку накладывается фильтр (например, по периоду или диапазону дат оплаты). В этом случае предыдущая строка может просто «выпасть» из запроса, так как она не соответствует заданному условию.

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

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

Решение 1. Левое соединение табличной части самой с собой по номеру строки

Если мы получаем данные непосредственно из табличной части конкретного документа, самым надежным и быстрым способом в запросе является использование стандартного реквизита НомерСтроки. Чтобы обойти проблему фильтрации дат, мы должны применить важный прием: накладывать ограничение по датам только на основную (левую) таблицу. Таблица для получения предыдущих значений (правая) должна соединяться без этого фильтра — только по ссылке на документ.

Проанализируем структуру такого запроса:


ВЫБРАТЬ
    ТЧ_Текущая.Ссылка КАК Ссылка,
    ТЧ_Текущая.НомерСтроки КАК НомерСтроки,
    ТЧ_Текущая.ДатаОплаты КАК ДатаОплаты,
    ТЧ_Текущая.Сумма КАК ТекущаяСумма,
    ЕСТЬNULL(ТЧ_Предыдущая.Сумма, 0) КАК ПредыдущаяСумма
ИЗ
    Документ.ИмяДокумента.ИмяТабличнойЧасти КАК ТЧ_Текущая
    
    // Присоединяем эту же табличную часть для поиска предыдущей строки
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ИмяДокумента.ИмяТабличнойЧасти КАК ТЧ_Предыдущая
        ПО ТЧ_Текущая.Ссылка = ТЧ_Предыдущая.Ссылка
        И ТЧ_Текущая.НомерСтроки = ТЧ_Предыдущая.НомерСтроки + 1

// Ограничение накладываем строго на левую (текущую) таблицу
ГДЕ
    ТЧ_Текущая.Ссылка = &Ссылка
    И ТЧ_Текущая.ДатаОплаты МЕЖДУ &НачалоПериода И &КонецПериода

Выясним, почему этот запрос работает корректно. Соединение выполняется по условию ТЧ_Текущая.НомерСтроки = ТЧ_Предыдущая.НомерСтроки + 1. Это означает, что для текущей строки с номером, например, 5, система будет искать в правой таблице строку с номером 4. Поскольку фильтр по датам в секции ГДЕ применяется только к ТЧ_Текущая, правая таблица сохраняет все свои строки. Даже если 4-я строка была заведена в прошлом месяце, её данные успешно подтянутся в результат запроса.

Решение 2. Использование возможностей СКД (функция ВычислитьВыражение)

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

Для вывода предыдущего значения выполним следующие шаги:

  1. Добавим в СКД вычисляемое поле (например, ПредыдущаяСумма).
  2. В колонке «Выражение» для этого поля укажем специальную функцию:

ВычислитьВыражение("Сумма", , , "Предыдущая", "Предыдущая")

Рассмотрим, как работает эта функция. Параметры "Предыдущая", "Предыдущая" указывают системе, что расчет выражения (в нашем случае — получение значения поля Сумма) должен производиться со смещением ровно на одну строку назад относительно текущей строки отчета. СКД делает это автоматически при формировании результата, учитывая все установленные пользователем отборы, сортировки и группировки.

Решение 3. Обработка через Таблицу Значений в коде

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

Посмотрим на пример реализации данного алгоритма:


// 1. Выбираем абсолютно все строки табличной части документа
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ТЧ.НомерСтроки КАК НомерСтроки,
    |   ТЧ.ДатаОплаты КАК ДатаОплаты,
    |   ТЧ.Сумма КАК Сумма
    |ИЗ
    |   Документ.ИмяДокумента.ИмяТабличнойЧасти КАК ТЧ
    |ГДЕ
    |   ТЧ.Ссылка = &Ссылка
    |УПОРЯДОЧИТЬ ПО
    |   НомерСтроки";

Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
ТЗ_Данные = Запрос.Выполнить().Выгрузить();

// 2. Обходим таблицу значений и применяем фильтр по дате программно
Для Каждого СтрокаТЧ Из ТЗ_Данные Цикл
    
    // Проверяем условие вхождения текущей строки в нужный период
    Если СтрокаТЧ.ДатаОплаты >= НачалоПериода И СтрокаТЧ.ДатаОплаты <= КонецПериода Тогда
        
        ИндексТекущей = ТЗ_Данные.Индекс(СтрокаТЧ);
        
        // Обязательно проверяем, существует ли предыдущая строка
        Если ИндексТекущей > 0 Тогда
            ПредыдущаяСтрока = ТЗ_Данные[ИндексТекущей - 1];
            
            // Теперь у нас есть доступ к предыдущей строке, даже если её дата вне периода
            Сообщить("Текущая: " + СтрокаТЧ.Сумма + ", Предыдущая: " + ПредыдущаяСтрока.Сумма);
        Иначе
            Сообщить("Текущая: " + СтрокаТЧ.Сумма + ", Предыдущей строки нет (это первая строка документа)");
        КонецЕсли;
        
    КонецЕсли;
КонецЦикла;

Преимущество этого метода в его абсолютной наглядности и простоте отладки. Мы гарантированно избегаем проблем с производительностью СУБД на сложных соединениях, перекладывая логику фильтрации на объект ТаблицаЗначений в оперативной памяти сервера.

Решение 4. Универсальный метод для таблиц без «Номера Строки» (через Временные Таблицы)

Что делать, если задача усложняется, и данные мы получаем не из табличной части конкретного документа (где есть гарантированное поле НомерСтроки), а, например, из периодического регистра сведений или регистра накопления? В таких таблицах последовательность строк определяется исключительно датой и временем (периодом).

Разберем по шагам универсальный алгоритм для запросов:

  1. Помещаем исходные данные во временную таблицу (например, ВТ_ИсходныеДанные).
  2. Соединяем эту временную таблицу саму с собой по условию: ВТ_Текущая.Период > ВТ_Предыдущая.Период.
  3. Группируем полученный результат по ключевым полям текущей строки и берем функцию МАКСИМУМ(ВТ_Предыдущая.Период). Таким образом, мы находим точную дату ближайшей предыдущей записи.
  4. На последнем этапе соединяем полученный результат с исходной таблицей по найденной максимальной предыдущей дате, чтобы получить физические значения ресурсов (сумм, количеств и т.д.).

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

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

← На главную