В процессе разработки документов часто возникает задача автоматизировать ввод данных, чтобы пользователю не приходилось вводить одну и ту же информацию дважды. Типичный пример — планирование цепочки задач в табличной части, где дата начала следующего этапа напрямую зависит от даты окончания предыдущего. В этой статье мы подробно разберем, как программно реализовать «цепную реакцию» дат в табличной части на управляемых формах.
Представим, что у нас есть табличная часть Расчет (или ПереченьНоменклатуры). В ней присутствуют реквизиты: ДатаПередачи (начало работ), СрокИсполнения (длительность в днях) и ДатаПлан (результат расчета). Нам необходимо, чтобы при расчете ДатаПлан в текущей строке, это же значение автоматически подставлялось в поле ДатаПередачи следующей строки. Рассмотрим, как это сделать правильно, избегая типичных ошибок с индексами.
Самый логичный момент для пересчета — событие ПриИзменении конкретного поля в табличной части. Когда пользователь меняет срок исполнения или дату начала, система должна мгновенно обновить текущую строку и «протолкнуть» данные дальше по списку. Проанализируем программный код, который позволяет это реализовать.
Для начала нам нужно получить текущие данные строки и рассчитать плановую дату. Важно помнить, что в 1С даты хранятся в секундах, поэтому количество дней нужно умножать на 86400 (количество секунд в сутках).
&НаКлиенте
Процедура РасчетСрокИсполненияПриИзменении(Элемент)
ТекущаяСтрока = Элементы.Расчет.ТекущиеДанные;
// Рассчитываем дату плана для текущей строки
Если ЗначениеЗаполнено(ТекущаяСтрока.ДатаПередачи) Тогда
ТекущаяСтрока.ДатаПлан = ТекущаяСтрока.ДатаПередачи + (ТекущаяСтрока.СрокИсполнения * 86400);
КонецЕсли;
// Запускаем механизм переноса даты в следующую строку
ПересчитатьПоследующиеСтроки(ТекущаяСтрока);
КонецПроцедуры
Если пользователь изменит дату в самой первой строке, это может повлиять на все последующие 50 строк документа. Чтобы не прописывать обновление только для одной «соседней» строки, правильнее использовать цикл, который пробежит от текущей позиции до конца таблицы. Рассмотрим, как реализовать такой алгоритм.
Выясним причину, почему нельзя просто использовать НомерСтроки. В 1С НомерСтроки начинается с 1, а индекс коллекции (массива строк) начинается с 0. Поэтому для обращения к следующей строке нам нужно вычислить её индекс через метод Индекс().
&НаКлиенте
Процедура ПересчитатьПоследующиеСтроки(СтартоваяСтрока)
ТЧ = Объект.Расчет;
ВсегоСтрок = ТЧ.Количество();
ТекущийИндекс = ТЧ.Индекс(СтартоваяСтрока);
// Начинаем цикл со следующей строки после измененной
Для Икс = ТекущийИндекс + 1 По ВсегоСтрок - 1 Цикл
ПредыдущаяСтрока = ТЧ[Икс - 1];
ТекущаяИтерация = ТЧ[Икс];
// Переносим план из предыдущей строки в начало текущей
ТекущаяИтерация.ДатаПередачи = ПредыдущаяСтрока.ДатаПлан;
// Сразу пересчитываем план текущей строки, чтобы следующая могла его забрать
ТекущаяИтерация.ДатаПлан = ТекущаяИтерация.ДатаПередачи + (ТекущаяИтерация.СрокИсполнения * 86400);
КонецЦикла;
КонецПроцедуры
Иногда пересчет всей таблицы не требуется, а нужно лишь, чтобы при нажатии кнопки «Добавить» в новой строке ДатаПередачи уже была заполнена данными из предыдущей. Для этого идеально подходит событие ПриНачалеРедактирования табличной части.
Разберем по шагам, как это работает:
НоваяСтрока = Истина).Количество() - 2.
&НаКлиенте
Процедура РасчетПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
Если НоваяСтрока И Не Копирование Тогда
КоличествоСтрок = Объект.Расчет.Количество();
Если КоличествоСтрок > 1 Тогда
// Индекс новой строки - это (Количество - 1),
// значит индекс предыдущей - (Количество - 2)
ПредыдущаяСтрока = Объект.Расчет[КоличествоСтрок - 2];
Элементы.Расчет.ТекущиеДанные.ДатаПередачи = ПредыдущаяСтрока.ДатаПлан;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию, с которой столкнулись участники форума: появление странных дат типа 01.01.0001 или 01.01.2011. Это происходит в тех случаях, когда программа пытается сложить «пустую» дату с числом или когда СрокИсполнения равен нулю.
Чтобы ваш код был стабильным, всегда придерживайтесь следующих правил:
ЗначениеЗаполнено() перед математическими операциями с датами.СрокИсполнения имеет числовой тип данных, иначе сложение приведет к ошибке исполнения кода.Объект.Расчет[Индекс] всегда проверяйте, что Индекс не меньше нуля и меньше общего количества строк.В реальном бизнесе срок исполнения часто считается не в календарных, а в рабочих днях. Простое прибавление 86400 * Срок проигнорирует выходные и праздники. Если в вашей конфигурации подключена БСП (Библиотека стандартных подсистем), рекомендуем заменить ручной расчет на вызов функции общего модуля.
Рассмотрим пример логики (обычно выполняется на сервере):
// Пример использования производственного календаря
Календарь = Справочники.ПроизводственныеКалендари.НайтиПоНаименованию("РоссийскаяФедерация");
ТекущаяСтрока.ДатаПлан = КалендарныеГрафики.ДобавитьКДатеПоКалендарю(ТекущаяСтрока.ДатаПередачи, ТекущаяСтрока.СрокИсполнения, Календарь);
Такой подход сделает вашу разработку профессиональной и надежной, а пользователи будут избавлены от необходимости вручную корректировать даты, выпадающие на воскресенье.
Подводя итог, автоматизация табличных частей через расчет индексов — это мощный инструмент. Главное — правильно выбрать событие (ПриИзменении для динамики или ПриНачалеРедактирования для новых данных) и обеспечить корректный обход коллекции строк.