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

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

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

Анализ исходной проблемы и причины ошибки

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


Процедура ТЧПЛПробегПоGPSПриИзменении(Элемент)
    Для Каждого СтрТЧ Из Объект.ТЧПЛ Цикл
        СтрТЧ.СпидометрВозвращения = СтрТЧ.СпидометрВыезда + СтрТЧ.ПробегПоGPS;
        СтрТЧ.СпидометрВыезда[Объект.ТЧПЛ.Индекс(СтрТЧ)+1] = СтрТЧ.СпидометрВозвращения;
    КонецЦикла;
КонецПроцедуры

Выясним причину возникновения ошибки «Получение элемента по индексу для значения не определено». Ошибка заключается в том, что разработчик пытается применить оператор получения по индексу [] не к самой табличной части, а к переменной СтрТЧ, которая является объектом текущей строки. Кроме того, при достижении последней строки индекс +1 выйдет за пределы коллекции, что также вызовет исключение. Проанализируем правильные подходы к решению этой задачи.

Способ №1: Использование переменной для хранения предыдущей строки

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


ПредыдущаяСтрока = Неопределено;

Для Каждого СтрТЧ Из Объект.ТЧПЛ Цикл
    // Если это не первая итерация, устанавливаем значение из предыдущей строки
    Если НЕ ПредыдущаяСтрока = Неопределено Тогда
        СтрТЧ.СпидометрВыезда = ПредыдущаяСтрока.СпидометрВозвращения;        
    КонецЕсли;

    // Рассчитываем текущий возврат
    СтрТЧ.СпидометрВозвращения = СтрТЧ.СпидометрВыезда + СтрТЧ.ПробегПоGPS;
    
    // Запоминаем текущую строку для использования на следующем шаге цикла
    ПредыдущаяСтрока = СтрТЧ;
КонецЦикла;

Преимущества данного метода:

  1. Отсутствие риска ошибки выхода за границы массива.
  2. Высокая скорость работы, так как нет лишних вызовов метода Индекс() или обращения к коллекции по номеру.
  3. Код легко читается и поддерживается.

Способ №2: Обращение к строке по индексу (НомерСтроки)

В платформе 1С каждая строка табличной части имеет свойство НомерСтроки. Важно помнить, что нумерация строк в интерфейсе начинается с 1, в то время как индексы в коллекции программного объекта — с 0. Рассмотрим, как реализовать логику через обращение к коллекции:


Для Каждого СтрТЧ Из Объект.ТЧПЛ Цикл
    Если СтрТЧ.НомерСтроки > 1 Тогда
        // Получаем предыдущую строку по индексу (НомерСтроки - 2)
        // НомерСтроки 2 имеет индекс 1, значит предыдущая (индекс 0) = 2 - 2
        СтрТЧ.СпидометрВыезда = Объект.ТЧПЛ[СтрТЧ.НомерСтроки - 2].СпидометрВозвращения;
    КонецЕсли;

    СтрТЧ.СпидометрВозвращения = СтрТЧ.СпидометрВыезда + СтрТЧ.ПробегПоGPS;
КонецЦикла;

Важное примечание: При использовании этого метода нужно быть осторожным. Если табличная часть отсортирована пользователем вручную в интерфейсе, физический порядок строк в коллекции Объект.ТЧПЛ может не совпадать с визуальным порядком НомерСтроки, если не было выполнено обновление индексов.

Оптимизация пересчета (продвинутый уровень)

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

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


&НаКлиенте
Процедура ТЧПЛПробегПоGPSПриИзменении(Элемент)
    ТекущаяСтрока = Элементы.ТЧПЛ.ТекущиеДанные;
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Сначала рассчитаем возврат для текущей строки
    ТекущаяСтрока.СпидометрВозвращения = ТекущаяСтрока.СпидометрВыезда + ТекущаяСтрока.ПробегПоGPS;
    
    // Определяем индекс текущей строки в коллекции
    ИндексНачала = Объект.ТЧПЛ.Индекс(ТекущаяСтрока);
    ВсегоСтрок = Объект.ТЧПЛ.Количество();
    
    // Запускаем цикл со следующей строки до самого конца
    Для и = ИндексНачала + 1 По ВсегоСтрок - 1 Цикл
        СтрокаДляОбработки = Объект.ТЧПЛ[и];
        СтрокаПредыдущая = Объект.ТЧПЛ[и - 1];
        
        СтрокаДляОбработки.СпидометрВыезда = СтрокаПредыдущая.СпидометрВозвращения;
        СтрокаДляОбработки.СпидометрВозвращения = СтрокаДляОбработки.СпидометрВыезда + СтрокаДляОбработки.ПробегПоGPS;
    КонецЦикла;
КонецПроцедуры

Обработка удаления и перемещения строк

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

  1. Удаление строки: Если пользователь удалит строку из середины, цепочка спидометра прервется. Необходимо в событии ПослеУдаления табличной части вызывать процедуру полного пересчета всей колонки.
  2. Перемещение строк: Кнопки «Вверх» и «Вниз» меняют порядок следования. В этом случае данные о выезде станут некорректными. Рекомендуется использовать событие ПриИзменении самой таблицы (которое срабатывает при перемещении) для актуализации данных.
  3. Первая строка: Для самой первой строки документа значение СпидометрВыезда обычно должно подтягиваться из данных информационной базы (например, из предыдущего путевого листа по данному автомобилю). Логично добавить проверку: если Индекс == 0, то выполнять запрос к серверу или искать последний закрытый документ.

Использование вышеописанных методов позволит создать надежный и удобный интерфейс для пользователя, минимизируя вероятность ввода ошибочных данных в учетную систему.

← На главную