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

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

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

Постановка задачи и анализ ситуации

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

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

Самый логичный момент для пересчета — событие ПриИзменении конкретного поля в табличной части. Когда пользователь меняет срок исполнения или дату начала, система должна мгновенно обновить текущую строку и «протолкнуть» данные дальше по списку. Проанализируем программный код, который позволяет это реализовать.

Для начала нам нужно получить текущие данные строки и рассчитать плановую дату. Важно помнить, что в 1С даты хранятся в секундах, поэтому количество дней нужно умножать на 86400 (количество секунд в сутках).


&НаКлиенте
Процедура РасчетСрокИсполненияПриИзменении(Элемент)
    ТекущаяСтрока = Элементы.Расчет.ТекущиеДанные;
    
    // Рассчитываем дату плана для текущей строки
    Если ЗначениеЗаполнено(ТекущаяСтрока.ДатаПередачи) Тогда
        ТекущаяСтрока.ДатаПлан = ТекущаяСтрока.ДатаПередачи + (ТекущаяСтрока.СрокИсполнения * 86400);
    КонецЕсли;
    
    // Запускаем механизм переноса даты в следующую строку
    ПересчитатьПоследующиеСтроки(ТекущаяСтрока);
КонецПроцедуры

Способ 2: Метод «Цепной реакции» (обход строк в цикле)

Если пользователь изменит дату в самой первой строке, это может повлиять на все последующие 50 строк документа. Чтобы не прописывать обновление только для одной «соседней» строки, правильнее использовать цикл, который пробежит от текущей позиции до конца таблицы. Рассмотрим, как реализовать такой алгоритм.

Выясним причину, почему нельзя просто использовать НомерСтроки. В 1С НомерСтроки начинается с 1, а индекс коллекции (массива строк) начинается с 0. Поэтому для обращения к следующей строке нам нужно вычислить её индекс через метод Индекс().


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

Способ 3: Заполнение при создании новой строки

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

Разберем по шагам, как это работает:

  1. Проверяем, является ли строка действительно новой (параметр НоваяСтрока = Истина).
  2. Проверяем, есть ли вообще в таблице строки (нельзя взять данные из «нулевой» строки).
  3. Получаем данные предыдущей строки по индексу Количество() - 2.

&НаКлиенте
Процедура РасчетПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
    Если НоваяСтрока И Не Копирование Тогда
        КоличествоСтрок = Объект.Расчет.Количество();
        Если КоличествоСтрок > 1 Тогда
            // Индекс новой строки - это (Количество - 1), 
            // значит индекс предыдущей - (Количество - 2)
            ПредыдущаяСтрока = Объект.Расчет[КоличествоСтрок - 2];
            
            Элементы.Расчет.ТекущиеДанные.ДатаПередачи = ПредыдущаяСтрока.ДатаПлан;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Важные нюансы и обработка ошибок

Проанализируем ситуацию, с которой столкнулись участники форума: появление странных дат типа 01.01.0001 или 01.01.2011. Это происходит в тех случаях, когда программа пытается сложить «пустую» дату с числом или когда СрокИсполнения равен нулю.

Чтобы ваш код был стабильным, всегда придерживайтесь следующих правил:

Продвинутый уровень: Учет рабочих дней

В реальном бизнесе срок исполнения часто считается не в календарных, а в рабочих днях. Простое прибавление 86400 * Срок проигнорирует выходные и праздники. Если в вашей конфигурации подключена БСП (Библиотека стандартных подсистем), рекомендуем заменить ручной расчет на вызов функции общего модуля.

Рассмотрим пример логики (обычно выполняется на сервере):


// Пример использования производственного календаря
Календарь = Справочники.ПроизводственныеКалендари.НайтиПоНаименованию("РоссийскаяФедерация");
ТекущаяСтрока.ДатаПлан = КалендарныеГрафики.ДобавитьКДатеПоКалендарю(ТекущаяСтрока.ДатаПередачи, ТекущаяСтрока.СрокИсполнения, Календарь);

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

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

← На главную